Reputation: 73
I need to create an array from multiple arrays. The new array must only contain the values that is present in all arrays passed in. For example.
array1 := []string{"hello", "germany", "brasil", "fiji"}
array2 := []string{"goodbye", "germany", "brasil", "fiji"}
array3 := []string{"hello", "brasil", "fiji"}
array4 := []string{"hello", "brasil", "fiji", "usa"}
func mergeArrays(arrs ...[]string) []string{
// process arrays
}
myNewArray := mergeArrays(array1,array2,array3,array4)
fmt.Println(myNewArray) // ["fiji", "brasil"]
The example should return ["fiji", "brasil"]
since they are the only values present in all arrays.
How could I go about writing a function that could achieve such a goal in golang?
This is my attempt but feels a bit clumsy
func mergeArrays(arrs ...[]string) []string {
var finalArr []string
if len(arrs) == 0 {
return finalArr
}
for i, a := range arrs {
if i == 0 {
finalArr = arrs[0]
continue
}
for i, e := range finalArr {
if !strContains(a, e) {
finalArr = append(finalArr[:i], finalArr[i+1:]...)
}
}
}
return finalArr
}
func strContains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
Playground link: https://play.golang.org/p/KRygw7OVBbn
Upvotes: 1
Views: 3524
Reputation: 1317
@colminator's is a valid answer but the algorithm is not optimal. @hallazang's suggestion is a right approach, but why creating 2 maps if we need only one? See the next option for better efficiency.
func repeatingItemsFromArrays(arrays ...[]string) []string {
la := len(arrays)
out := []string{}
// handle corner cases for efficiency
if la == 0 {
return out
}
if la == 1 {
return arrays[0]
}
m := make(map[string]int)
for i := range arrays {
for j := range arrays[i] {
m[arrays[i][j]]++
}
}
for k, v := range m {
if v == la {
out = append(out, k)
}
}
return out
}
Upvotes: 0
Reputation: 701
Idea:
arr
).len(arr)
, the item presents in all arrays.Here's an example that employees this approach:
package main
import "fmt"
func uniq(arr []string) []string {
cache := make(map[string]struct{})
for _, s := range arr {
cache[s] = struct{}{}
}
var r []string
for s := range cache {
r = append(r, s)
}
return r
}
func mergeArrays(arrs ...[]string) []string {
count := make(map[string]int)
for _, arr := range arrs {
for _, s := range uniq(arr) {
count[s]++
}
}
var merged []string
for s, n := range count {
if n == len(arrs) {
merged = append(merged, s)
}
}
return merged
}
func main() {
array1 := []string{"hello", "germany", "brasil", "fiji"}
array2 := []string{"goodbye", "germany", "brasil", "fiji"}
array3 := []string{"hello", "brasil", "fiji"}
array4 := []string{"hello", "brasil", "fiji", "usa"}
myNewArray := mergeArrays(array1, array2, array3, array4)
fmt.Println(myNewArray) // ["fiji", "brasil"]
}
And playground link: https://play.golang.org/p/FB3wJ7-gaIa
EDIT: it will work properly even if there's any duplicate in each array.
Upvotes: 0
Reputation: 22037
Per my comment above, here's one way to do it with go map
s and thus avoid iterating over potentially large slices:
func itemize(a []string) map[string]struct{} {
m := make(map[string]struct{})
for _, v:=range a {
m[v] = struct{}{} // struct{}{} == an empty struct (i.e. a value that incurs no storage)
}
return m
}
func commonElements(arrs ...[]string) (results []string) {
if len(arrs) == 0 {
return // edge case
}
mm := itemize(arrs[0]) // master map
for i:=1; i<len(arrs);i++ {
m := itemize(arrs[i]) // current map
for k := range mm {
if _, ok := m[k]; !ok {
delete(mm, k) // master item not in current slice, so remove from master
}
}
}
results = make([]string, len(mm)) // make a precisely sized slice...
i:=0
for k := range mm {
results[i] = k // so we can insert results directly into it without using append
i++
}
return
}
https://play.golang.org/p/pTaXR-nY9zm
Upvotes: 3