loading...

In_array alternative for Golang. Searching value in 'map, array, slice'.

ashkan90 profile image Emirhan Updated on ・2 min read

Hi, I started golang nowadays. I moved from PHP to Golang and I can say that Golang is too beautiful.
I didn't know there's not search function exists until I decided to write a package.
Basically in PHP, we might be implement our 'in_array' alternative like this;

if (is_array($x)) {
   foreach ($x as $v) {
      if ($v == $searchVal) { return true; }
   }
}

Of course, This alternative can be used non-multidimensional.
In Golang, we cannot decide type of variable easily. There's a package called 'Reflect'.
Reflect package is not used only getting the type of any variable. Its documentation is here.

If we understand what we gonna do then we can do!
Let's write the func prototype;

func In_array(search interface{}, array interface{}) bool

No body want to restrict the search value's type because we want to search any type, any time. Because of that, I used 'interface{}'.
As same as search, incoming value(which is array) must be interface{} too.

import (
    "reflect"
)

func In_array(search interface{}, array interface{}) bool {

    val := reflect.ValueOf(array)
    val = val.Convert(val.Type())

    typ := reflect.TypeOf(array).Kind()

    switch typ {
    case reflect.Map:
        s := val.MapRange()

        for s.Next() {
            s.Value().Convert(s.Value().Type())
            for i := 0; i < s.Value().Len(); i++ {
                if deep {
                    if reflect.DeepEqual(search, s.Value().Index(i).Interface()) {
                        return true
                    }
                } else {
                    str := s.Value().Index(i).String()
                    if strings.Contains(str, search.(string)) {
                        return true
                    }
                }
            }
        }
    case reflect.Slice, reflect.Array:
        for i := 0; i < val.Len(); i++ {
            if reflect.DeepEqual(search, val.Index(i).Interface()) {
                return true
            }
        }
    }

    return false
}

We're detecting type of value with this line of code;

reflect.TypeOf(array).Kind()

This code, prepare 'reflect' package's constants they might be;

Map,
Array,
Slice,
String,
....

We should iterate the Value by MapRange() if Value is Map
(don't forget, we're not doing recursive stuff. This is just a idea)
Then we're looping current index's value and checking if given search value is equal to map's value.
It's same as nested loops.

I used the DeepEqual() because,
Docs:
DeepEqual reports whether x and y are “deeply equal,” defined as follows. Two values of identical type are deeply equal if one of the following cases applies. Values of distinct types are never deeply equal.

And BUM! This is simple and efficient. We can test it with this;

rules := map[string][]string{
    "name":    {"must-be-string", "min-len-20"},
    "surname": {"must-be-string", "min-len-15"},
}
in_array("must-be-string", rules) // true

There're some changes made because of deep and logical problems. I updated the code so you can use it with ease.

Here's the source code,

GitHub logo ashkan90 / golang-in_array

PHP in_array alternative for golang

golang-in_array

PHP in_array alternative for golang

It's tested with

  • "search any type in array of map"
  • [] "any type in recursive map (no depth)"
  • "any type in array"
  • "any type in slice"

Posted on by:

ashkan90 profile

Emirhan

@ashkan90

Profession in learning

Discussion

markdown guide
 

This code is interesting and it translate the concepts from un-typed variables in PHP to Go accurately, but typically I don't see Go programmers structuring programs in this way.

Since Go is statically typed, you want function args and return values to have their types spelled out. Very rarely do I write code where I do not know the types. Generally, using interface{} is like using void* in C, it's a catch-all and a sign that perhaps you need to structure your code or logic differently.

Just sharing these thoughts since you're learning the language. The code you shared is useful and implements an untyped searched well, but I would strongly recommend to avoid using programming idioms from other languages and re-implementing them Go. When I transitioned from C++ to Go I initially tried to do things the C++ way and experienced this myself.

 

Thanks for your thoughts, I'm really appreciated that you're giving me advice. I understand what you're talking about. I'll search for other lang. implementations and try to them Go. I think your way of getting experience is very efficient. Again, thank you.