From b40d10a3666cadd446881bd9be5a10e6509fbf38 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Thu, 21 Nov 2024 02:54:10 -0500 Subject: [PATCH] util: Add a generic map key and value swapping function Fun little utility function which is useful. --- util/generics.go | 47 ++++++++++++++++++++++ util/generics_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 util/generics.go create mode 100644 util/generics_test.go diff --git a/util/generics.go b/util/generics.go new file mode 100644 index 00000000..2935c1bb --- /dev/null +++ b/util/generics.go @@ -0,0 +1,47 @@ +// Mgmt +// Copyright (C) 2013-2024+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this program, or any covered work, by linking or combining it +// with embedded mcl code and modules (and that the embedded mcl code and +// modules which link with this program, contain a copy of their source code in +// the authoritative form) containing parts covered by the terms of any other +// license, the licensors of this program grant you additional permission to +// convey the resulting work. Furthermore, the licensors of this program grant +// the original author, James Shubin, additional permission to update this +// additional permission if he deems it necessary to achieve the goals of this +// additional permission. + +package util + +import ( + "fmt" +) + +// MapSwap returns a new map with the keys and values interchanged. It errors if +// doing so would cause a loss of a value. This is a generic version. +func MapSwap[M ~map[K]V, K, V comparable](m M) (map[V]K, error) { + ret := make(map[V]K) + for k, v := range m { + if _, exists := ret[v]; exists { + return nil, fmt.Errorf("duplicate value: %v", v) + } + ret[v] = k + } + return ret, nil +} diff --git a/util/generics_test.go b/util/generics_test.go new file mode 100644 index 00000000..a75f6364 --- /dev/null +++ b/util/generics_test.go @@ -0,0 +1,93 @@ +// Mgmt +// Copyright (C) 2013-2024+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this program, or any covered work, by linking or combining it +// with embedded mcl code and modules (and that the embedded mcl code and +// modules which link with this program, contain a copy of their source code in +// the authoritative form) containing parts covered by the terms of any other +// license, the licensors of this program grant you additional permission to +// convey the resulting work. Furthermore, the licensors of this program grant +// the original author, James Shubin, additional permission to update this +// additional permission if he deems it necessary to achieve the goals of this +// additional permission. + +//go:build !root + +package util + +import ( + "reflect" + "testing" +) + +func TestMapSwap1(t *testing.T) { + m := map[string]int{ + "zero": 0, + "one": 1, + "two": 2, + } + ret, err := MapSwap(m) + if err != nil { + t.Errorf("test failed with: %v", err) + return + } + + exp := map[int]string{ + 0: "zero", + 1: "one", + 2: "two", + } + if !reflect.DeepEqual(exp, ret) { + t.Errorf("expected: %v, actual: %v", exp, ret) + } +} + +func TestMapSwap2(t *testing.T) { + m := map[int]string{ + 0: "zero", + 1: "one", + 2: "two", + } + ret, err := MapSwap(m) + if err != nil { + t.Errorf("test failed with: %v", err) + return + } + + exp := map[string]int{ + "zero": 0, + "one": 1, + "two": 2, + } + if !reflect.DeepEqual(exp, ret) { + t.Errorf("expected: %v, actual: %v", exp, ret) + } +} + +func TestMapSwap3(t *testing.T) { + m := map[int]string{ + 0: "zero", + 1: "one", + 2: "zero", // dupe! + } + ret, err := MapSwap(m) + if err == nil { + t.Errorf("test expected failure but passed with: %v", ret) + } +}