Shorter/clearer way of extracting common and unique elements from a map


(Pritesh Ugrankar) #1

Hi Gophers,
I once again seek your guidance in solving this problem. The problem is as follows:
Given two maps, write a program that would return common elements (if any) and unique elements(if any) in two separate maps.
Here’s what I’ve done so far:

package main

import "fmt"

func main() {
	fmt.Println("vim-go")

	firstMap := map[string]string{
		"first":    "Pritesh",
		"middle":   "Manohar",
		"last":     "Ugrankar",
		"city":     "Bangalore",
		"building": "Ittina Neela",
	}
	secondMap := map[string]string{
		"first":    "Pritesh",
		"middle":   "Manohar",
		"last":     "Ugrankar",
		"location": "ECity",
	}

	common, unique := unique(firstMap, secondMap)
	fmt.Println("First map contains:", firstMap)
	fmt.Println("Second map contains:", secondMap)
	fmt.Println("Common elements are:", common)
	fmt.Println("Unique elements are:", unique)
}

func unique(first map[string]string, second map[string]string) (map[string]string, map[string]string) {
	commonMap := make(map[string]string)
	uniqueMap := make(map[string]string)

	for key, _ := range first {
		uniqueMap[key] = first[key]
	}
	for key, _ := range second {
		if _, ok := uniqueMap[key]; ok == true {
			delete(uniqueMap, key)
			commonMap[key] = second[key]
		} else {
			uniqueMap[key] = second[key]
		}
	}

	return commonMap, uniqueMap
}

And I get the answer as expected:

pritesh@debian:~/go/src/github.com/pritesh-ugrankar/umaps$ go run main.go
vim-go
First map contains: map[building:Ittina Neela city:Bangalore first:Pritesh last:Ugrankar middle:Manohar]
Second map contains: map[first:Pritesh last:Ugrankar location:ECity middle:Manohar]
Common elements are: map[first:Pritesh last:Ugrankar middle:Manohar]
Unique elements are: map[building:Ittina Neela city:Bangalore location:ECity]
pritesh@debian:~/go/src/github.com/pritesh-ugrankar/umaps$ 

My problem is with the following section in the code:

for key, _ := range second {
		if _, ok := uniqueMap[key]; ok == true {
			delete(uniqueMap, key)
			commonMap[key] = second[key]
		} else {
			uniqueMap[key] = second[key]

Problem is if I remove the “else” loop, then the unique key from the second map does not get added to the unique map. I’ve tried everything but nothing works except this, and this looks really ugly like a shoddy workaround.
How can I make it better??


(Yamil Bracho) #2

What I love about Go is simplicity as driving force( Experiment, Simplify, Ship)
You can simplify your function just doing something like this :

func unique(first map[string]string, second map[string]string) (map[string]string, map[string]string) {
	commonMap := make(map[string]string)
	uniqueMap := make(map[string]string)
	
	for key, value := range first {
	   // Check if unique or not
	   if   _, ok := second[key]; ok {
	      // Exists in second, so it is common
	     commonMap[key] = value
	    } else {
	      // Does not exist in secodn, it is unique
	      uniqueMap[key] = value
	    }
	}

	return commonMap, uniqueMap
}

(Pritesh Ugrankar) #3

Thank you Yamil for your answer and your patience.
One of those “dammit why couldn’t I think like this” moment for me.
I’ve learnt far more from your answers than any book or video.


(Yamil Bracho) #4

Thank you especially for your questions, which make me think and learn more …:ok_hand:


(Pritesh Ugrankar) #5

After seeing your efficient answers my questions and answers both look dumb to me. Hehehehe.


(Pritesh Ugrankar) #6

Hi Yamil,
I tried the code you pasted, but it misses some values:
Here’s the code (I’ve commented out my code and input yours)

package main

import "fmt"

func main() {
	fmt.Println("vim-go")

	firstMap := map[string]string{
		"first":    "Pritesh",
		"middle":   "Manohar",
		"last":     "Ugrankar",
		"city":     "Bangalore",
		"building": "Ittina Neela",
	}
	secondMap := map[string]string{
		"first":    "Pritesh",
		"middle":   "Manohar",
		"last":     "Ugrankar",
		"location": "ECity",
	}

	common, unique := unique(firstMap, secondMap)
	fmt.Println("First map contains:", firstMap)
	fmt.Println("Second map contains:", secondMap)
	fmt.Println("Common elements are:", common)
	fmt.Println("Unique elements are:", unique)
}

func unique(first map[string]string, second map[string]string) (map[string]string, map[string]string) {
	commonMap := make(map[string]string)
	uniqueMap := make(map[string]string)

	/*
		for key, _ := range first {
			uniqueMap[key] = first[key]
		}
		for key, _ := range second {
			if _, ok := uniqueMap[key]; ok == true {
				delete(uniqueMap, key)
				commonMap[key] = second[key]
			} else {
				uniqueMap[key] = second[key]
			}
		}
	*/
	for key, value := range first {
		// Check if unique or not
		if _, ok := second[key]; ok {
			// Exists in second, so it is common
			commonMap[key] = value
		} else {
			// Does not exist in secodn, it is unique
			uniqueMap[key] = value
		}
	}
	return commonMap, uniqueMap
}

And the output is:

pritesh@debian:~/go/src/github.com/pritesh-ugrankar/umaps$ go run main.go
vim-go
First map contains: map[building:Ittina Neela city:Bangalore first:Pritesh last:Ugrankar middle:Manohar]
Second map contains: map[first:Pritesh last:Ugrankar **location:ECity** middle:Manohar]
Common elements are: map[first:Pritesh last:Ugrankar middle:Manohar]
Unique elements are: map[building:Ittina Neela city:Bangalore]

Note that the location:ECity entry is missing. (I’ve highlighted it with ** in the code output).


(Yamil Bracho) #7

OK.
The only check is from first map to the second, I mean, we only check if a element is in map first and not in the second, but the opposite is not done.
Just add the following code before returning maps in the unique function

// Now search for elements only in second mao and not in first map
	for key, value := range second {
	   if _, ok := first[key]; !ok {
	     uniqueMap[key] = value
	   }
	}
	
	return commonMap, uniqueMap

(Pritesh Ugrankar) #8

Hi Yamil,
Indeed. Right after typing the question, I tried to solve it myself and wrote almost the same code except I wrote ok == false instead of !ok (same thing, different syntax).

Thank you once again Sir.