Say I have values a
, b
and c
. I want to find out if they are equal. If I do
if a == b == c{...}
Then I get a compile error
invalid operation: a == b == c (mismatched types bool and TypeOfABandC)
This is pretty obvious, because this parses to:
(a == b) == c
And (a == b)
is a bool.
Of course I can do:
if a == b && a == c {...}
However, this isn't very nice looking and feels confusing. Is there another way?
A note beforehand:
Your last proposed solution is the shortest, clearest and most efficient way to compare if 3 values are equal:
if a == b && a == c {
fmt.Println("Clearest: all 3 are equal")
}
or alternatively (to your liking):
if a == b && b == c {
fmt.Println("Clearest: all 3 are equal")
}
The rest of this answer (what follows) is just toying with the language specification and the language's capabilities, presenting what I find fun and creative. They do not attempt to provide a superior solution.
Try all the examples below on the Go Playground. The examples build on the terms and the result of the comparisons which are defined in Spec: Comparison operators.
General note: in the examples below I used the type interface{}
which will work whatever type your values have (a
, b
and c
), but if you know they are of type int
for example, you can use that specific type too (which would improve efficiency and shorten the length of the examples).
map
as a setif len(map[interface{}]int{a: 0, b: 0, c: 0}) == 1 {
fmt.Println("Map set: all 3 are equal")
}
Effectively we put all comparable values into a map
as keys, and if all are equal, there will be only 1 pair in the map, so the "length" of the map will be 1. The value type of the map doesn't play any role here, it could be anything. I used int
because this results in the shortest composite literal (that defines the map
value).
This solution is flexible as you can use any number of values to test if all are equal, not just 3.
Arrays are comparable (unlike slices):
if [2]interface{}{a, b} == [2]interface{}{b, c} {
fmt.Println("Arrays: all 3 are equal")
}
The result of the array comparison will be true
if a == b
and b == c
(if the corresponding elements are equal).
Note that you can apply this method on any number of values as well. It would look like this for 5 values:
if [4]interface{}{a, b, c, d} == [4]interface{}{b, c, d, e} {
fmt.Println("Arrays: all 5 are equal")
}
map
if map[interface{}]bool{a: b == c}[b] {
fmt.Println("Tricky map: all 3 are equal")
}
This composite literal will assign the comparision result of b == c
to the key a
. And we ask the value associated with the key b
. If a == b
, the result of the indexing expression will be the result of b == c
, that is, whether all 3 values are equal. If a != b
, then the zero value of the value type will be the result, which is false
in case of bool
, properly telling that all 3 values are not equal.
struct
sstruct
values are also comparable, so:
if struct{ a, b interface{} }{a, b} == struct{ a, b interface{} }{b, c} {
fmt.Println("Anon structs: all 3 are equal")
}
struct
sIf we define a simple struct
beforehand:
type P struct{ a, b interface{} }
Comparison will be much more compact:
if (P{a, b} == P{b, c}) {
fmt.Println("Structs: all 3 are equal")
}
(Note that the expression of the if
statement must be put in parenthesis to avoid parsing ambiguity - else it's a compile time error!)
Slices are not comparable, so we will need to borrow some help from reflect.DeepEqual()
:
if reflect.DeepEqual([]interface{}{a, b}, []interface{}{b, c}) {
fmt.Println("Slices: all 3 are equal")
}
func AllEquals(v ...interface{}) bool {
if len(v) > 1 {
a := v[0]
for _, s := range v {
if a != s {
return false
}
}
}
return true
}
And using it:
if AllEquals(a, b, c) {
fmt.Println("Helper function: all 3 are equal")
}
This solution is also flexible, you can call AllEquals()
with any number of values.
Note that we can make the AllEquals()
function more compact if we're also willing to call reflect.DeepEqual()
here:
func AllEquals2(v ...interface{}) bool {
if len(v) < 2 {
return true
}
return reflect.DeepEqual(v[:len(v)-1], v[1:])
}