Open1

Goの標準ライブラリにmapsというものを今更ながら知った

りょうネコりょうネコ

https://pkg.go.dev/maps

マップのコピー、キーの削除、比較などの操作が簡単に行えそう

主な機能としては:

  • maps.Clone - マップのコピーを作成
  • maps.Equal - 2つのマップが等しいかどうか比較
  • maps.EqualFunc - カスタム比較関数を使用してマップを比較
  • maps.DeleteFunc - 条件に合うキーと値のペアを削除
  • maps.Copy - ソースマップのすべてのキー/値ペアのコピーを作成

Equal

func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
  • 目的: 2つのマップが同じキー/値ペアを持つかどうかを確認します
  • ジェネリクス: M1M2map[K]V型、KVは比較可能な型
  • 動作:
    1. まずマップのサイズが異なる場合は即座にfalseを返します
    2. m1の各キーと値に対して、同じキーがm2に存在し、かつ値が等しいか(==演算子で比較)を確認します
    3. すべてのキー/値ペアが一致すればtrueを返します
  • 使用例: 2つの設定マップが同じかどうかの確認など

EqualFunc

func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool
  • 目的: カスタム比較関数を使用して2つのマップを比較します
  • ジェネリクス:
    • M1map[K]V1型、M2map[K]V2
    • Kは比較可能な型、V1V2は任意の型
  • 動作:
    1. Equalと同様にマップサイズを比較
    2. 値の比較に==の代わりに、渡されたeq関数を使用
  • 使用ケース: 値が複雑な構造体の場合や、特定のフィールドのみを比較したい場合に便利

Clone

func Clone[M ~map[K]V, K comparable, V any](m M) M
  • 目的: マップのコピーを作成します(シャローコピー)
  • ジェネリクス: Mmap[K]V型、Kは比較可能な型、Vは任意の型
  • 動作:
    1. 元のマップがnilの場合はnilを返します
    2. それ以外の場合、内部のclone関数を使用して新しいマップを作成
  • シャローコピー: ネストされたマップや構造体はコピーされません(参照のみコピー)

Copy

func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2)
  • 目的: ソースマップ(src)のすべてのキー/値ペアを宛先マップ(dst)にコピーします
  • ジェネリクス: M1M2map[K]V型、Kは比較可能な型、Vは任意の型
  • 動作:
    1. srcの各キー/値ペアをdstに追加します
    2. キーが既に存在する場合は、値が上書きされます
  • 違い: Cloneが新しいマップを作成するのに対し、Copyは既存のマップに追加します

DeleteFunc

func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool)
  • 目的: 指定された条件を満たすキー/値ペアをマップから削除します
  • ジェネリクス: Mmap[K]V型、Kは比較可能な型、Vは任意の型
  • 動作:
    1. マップの各キー/値ペアに対して、渡されたdel関数を呼び出します
    2. del関数がtrueを返す場合、そのキー/値ペアを削除します
  • 使用例: 特定の条件(古すぎる値、無効な値など)に基づいてマップをフィルタリングする場合

これらの関数は、マップ操作を簡素化し、コード量を減らすことができるため、Go 1.21以降のプロジェクトでは積極的に活用することをお勧めします。

実コード

package maps

import (
	_ "unsafe"
)

// Equal reports whether two maps contain the same key/value pairs.
// Values are compared using ==.
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
	if len(m1) != len(m2) {
		return false
	}
	for k, v1 := range m1 {
		if v2, ok := m2[k]; !ok || v1 != v2 {
			return false
		}
	}
	return true
}

// EqualFunc is like Equal, but compares values using eq.
// Keys are still compared with ==.
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool {
	if len(m1) != len(m2) {
		return false
	}
	for k, v1 := range m1 {
		if v2, ok := m2[k]; !ok || !eq(v1, v2) {
			return false
		}
	}
	return true
}

// clone is implemented in the runtime package.
//
//go:linkname clone maps.clone
func clone(m any) any

// Clone returns a copy of m.  This is a shallow clone:
// the new keys and values are set using ordinary assignment.
func Clone[M ~map[K]V, K comparable, V any](m M) M {
	// Preserve nil in case it matters.
	if m == nil {
		return nil
	}
	return clone(m).(M)
}

// Copy copies all key/value pairs in src adding them to dst.
// When a key in src is already present in dst,
// the value in dst will be overwritten by the value associated
// with the key in src.
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
	for k, v := range src {
		dst[k] = v
	}
}

// DeleteFunc deletes any key/value pairs from m for which del returns true.
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) {
	for k, v := range m {
		if del(k, v) {
			delete(m, k)
		}
	}
}