I'm searching for something like a both way map

Hi!
Maybe it sounds a little bit strange, but I search something like a both way map type.
I’m trying to describe what I mean by that:

Let’s assume I have a var token = make(map[string]string), and one entry in this map could be token[“A”] = "nasty"
With this map I’m be able to lookup easily for “A” and get a “nasty” if i use var mystring = token[“A”]
But how could it work if I want to do it also the other direction?
Like I ask for “nasty” and will get an “A” for it?

Should I feed the map with vice versa token[“A”] = “nasty” and "token[“nasty”] = “A” or would it be better to use a struct with two text fields instead and fill an slice with it in which I can search?

Any thoughts would be appreciated.

you probably look for something like that (reverse a map code from here).

1 Like

Hey @gekkonier. I just threw this together for you on https://play.golang.org.

I like @geosoft1’s answer for you but also thought you might like a different method as well that doesn’t necessarily require you to have to do any map reversals and thought that if you’d like to separate being able to search a map using keys and/or values, here’s a simple way for you to help get your head around doing that.

Here’s the playground link. https://play.golang.org/p/4c3UgqNHTZ.

Edit: As a side note, both @geosoft1’s method and my own will only return the first value found in the map. Generally map keys can’t be duplicates, however map values can. Therefore if you need something that returns all values found and not just the first value it finds (if searching by value and not by key), you can just let me know and I can change the code to make that work for you.

I’ll also paste the code here in case you just want a quick look:

package main

import "fmt"

func main() {
	// Create a new map m.
	m := map[string]string{
		"one":   "1",
		"two":   "2",
		"three": "3",
		"four":  "4",
		"five":  "5",
	}

	// Find in map by value
	fmt.Println(findByValue(m, "1"))
	fmt.Println(findByValue(m, "10"))

	// Find in map by key
	fmt.Println(findByKey(m, "one"))
	fmt.Println(findByKey(m, "ten"))

	// Generic find in map. Searches using keys and values.
	fmt.Println(findInMap(m, "five"))
	fmt.Println(findInMap(m, "5"))
	fmt.Println(findInMap(m, "twenty"))
	fmt.Println(findInMap(m, "20"))
}

// findInMap uses findByValue and findByKey to check whether a
// string exists either as a key or a value inside of a map.
//
// findInMap returns either the key or value, if the string was found
// in the map, or an empty string if neither were found.
func findInMap(m map[string]string, str string) string {
	// Check if str exists as a value in map m.
	if val := findByKey(m, str); val != "" {
		return val
	}

	// Check if str exists as a key in map m.
	if val := findByValue(m, str); val != "" {
		return val
	}

	// str doesn't exist in map m.
	return ""
}

// findByValue searches for a string in a map based on map values.
//
// findByValue returns the key if the value was found, or an empty
// string if it wasn't.
func findByValue(m map[string]string, value string) string {
	for key, val := range m {
		if val == value {
			return key
		}
	}
	return ""
}

// findByKey searches for a string in a map based on map keys.
//
// findByKey returns the value if the key was found, or an empty
// string if it wasn't.
func findByKey(m map[string]string, key string) string {
	if val, found := m[key]; found {
		return val
	}
	return ""
}

Let me know if you have any questions!

2 Likes

@geosoft1 @radovskyb
I really appreciate your suggestions! I thank you for your ideas, they help me really a lot!

1 Like

The suggested approaches have linear time lookup on reverse lookups. What you probably want to do is have two maps and when adding a pair add the pair to both maps m1[k], m2[v] = v, k

2 Likes

Hey @voutasaurus. I completely agree that would be a much faster approach.

The main reason I decided to write my example as above was for ease of understanding and for extensibility knowing that if there is a need for value lookups in a map, he may need to retrieve multiple keys per value but with 2 maps there can only be one key per value lookup. I probably jumped the gun with that thought though and I agree your solution would be simpler and faster for what he is probably asking for.

Hey @radovskyb

It actually took me a quite a while to decide this was a good way. I’ve run into this requirement before and it definitely wasn’t obvious that using a composite data structure made more sense than implementing accessors for the simpler data structure.

The two maps approach can be good if you encapsulate the logic and make sure you’re not ever setting one without the other and make sure that storing duplicates isn’t required (if it is you need to think harder :P).

1 Like

Hey again @gekkonier.

I just quickly put together another example based on what @voutasaurus and I were discussing, just in case you or anybody else would like to know how to set up a bidirectional map the way mentioned above.

This example is still only a another simple quick write up and could be improved in numerous ways. It’s kept simple for learning purposes. It is however much faster than my previous example.

Check it out here.


Edit: Here are some very simple benchmarks based on both examples.

Old example:

BenchmarkFindByKey — 20000000 – 32.8 ns/op
BenchmarkFindByValue – 5000000 – 179 ns/op
BenchmarkFindInMap ----- 2000000 – 399 ns/op

New example:

BenchmarkFindByKey — 20000000 – 34.3 ns/op
BenchmarkFindByValue - 30000000 – 25.9 ns/op
BenchmarkFind -------------- 5000000 – 140 ns/op

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.