-2

I want to create a utility-function that is able to merge two given slices, determining equality by a given function.

type IsEqualTest func(interface{}, interface{}) bool

func ArrayMerge(one *[]interface{}, another *[]interface{}, comp IsEqualTest) *[]interface{} {
    merged := *one

    for _, element := range *another {
        if !ArrayContains(one, &element, comp) {
            merged = append(merged, element)
        }
    }

    return &merged
}

func ArrayContains(container *[]interface{}, eventualContent *interface{}, comp IsEqualTest) bool {
    for _, element := range *container {
        if comp(element, eventualContent) {
            return true
        }
    }

    return false
}

// please don't mind the algorithmic flaws

However, as go does treat the []interface{} type as non-compatible to slices of anything (and it lacks generics), I would need to iterate over both operands, converting the type of the contained elements when calling, which is not what anyone could want.

What is the Go style of dealing with collections containing any type?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
tuberains
  • 191
  • 9
  • 2
    Note that your code is dealing with slices, not arrays. – Jonathan Hall Dec 07 '19 at 19:50
  • 2
    "I would need to iterate over both" -- Yes, this is exactly the Go way. – Jonathan Hall Dec 07 '19 at 19:53
  • 4
    Note that because slices are already pointers, I see no need to have a pointer to a slice in your situation. – Eyal Dec 07 '19 at 19:57
  • 1
    Possible duplicate of https://stackoverflow.com/q/12753805/13860 – Jonathan Hall Dec 07 '19 at 19:58
  • It might be better to have them instead provide a function which returns, say, a string which uniquely identifies it. If two objects provide the same string, they are equal. Then you could use a `map[string]bool` to know if it exists in your slice - simply check if the key already exists, if not, add it to the map and add the item to the slice you will be returning. – dave Dec 07 '19 at 20:30

1 Answers1

1

First: without generics, there is no idiomatic way of doing this. Second: your thinking might be too influenced by other languages. You already got a function to compare, why not take it a bit further?

What I suggest below is not efficient, and it should not be done. However, if you really want to do it:

It looks like this is not a set union, but add the elements of the second slice to the first if they don't already exist in the first slice. To do that, you can pass two functions:

func merge(len1,len2 int, eq func(int,int)bool, write func(int)) {
   for i2:=0;i2<len2;i2++ {
     found:=false
     for i1:=0;i1<len1;i1++ {
        if eq(i1,i2) {
          found=true
          break
        }
     }
     if !found {
       write(i2)
     }
}

Above, eq(i,j) returns true if slice1[i]==slice2[j], and write(j) does append(result,slice2[j]).

Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • 1
    "there is no idiomatic way" --- **the** idiomatic way is copy-paste. – zerkms Dec 07 '19 at 21:35
  • The first part sounds like an acceptable answer. But why would anyone use go in production then. This leads to unmaintainable code as the codebase grows, doesn't it? – tuberains Dec 08 '19 at 12:16
  • Evidence shows otherwise. DRY is a perfect way too add dependencies and coupling between otherwise unrelated code. This algorithm you have, for instance, adds the second slice into the first for non-existing elements. How many of the users of this algorihtm will assume the outcome is a set (it is not if the first slice is not a set)? How many will fix it? How many other users will that fix break? Some level of repetition improves code readability and maintainability. This is just simpler when repeated. Of course, this is just opinion. If you want to see a large codebase, see k8s. – Burak Serdar Dec 08 '19 at 17:52