Проверка равенства двух срезов

274

Как я могу проверить, равны ли два среза?

wei2912
источник
111
Вопрос на самом деле о простой задаче, но IMO - это реальный вопрос с очень конкретным ответом. Как я мог видеть, что это могло быть закрыто как «ненастоящий вопрос» людьми, которых я не помню, когда-либо активно участвовавших в помеченных вопросами «Го», мне не под силу. В частности: вопрос не является двусмысленным, полным, узким для одной (хотя и простой) проблемы, не риторическим, и на него можно ответить точно и точно в его нынешнем виде. ==Оператор определен в Go только для некоторых типов, поэтому , кроме того, этот вопрос также является законным.
zzzz
4
Тем не менее, это не что-то из упомянутых в тесной причине («не может быть разумно ответить в его нынешнем виде»).
Rich Churcher
9
Хахаха, я не могу поверить, что это закрыто за "не реальный вопрос". 1) Нетрудно сказать, о чем спрашивают. 2) Вопрос не является двусмысленным / неполным / широким / необоснованным. Это довольно злоупотребление!
weberc2
5
Похоже, что в настоящее время слишком легко перепутать кнопку Downvote («Я думаю, что этот вопрос не требует усилий и его не очень хорошо задают») с кнопкой «Закрыть» («Я думаю, что на нее нельзя ответить по следующей причине ...» ".). Может быть потому, что голоса "за" свободны.
Кос
3
Получил развитие в Go и столкнулся с ним slice can only be compared to nil, и мне было интересно, есть ли идиоматический способ golang для проверки равенства срезов ... если оператор равенства не определен языком, то я считаю разумным спросить наиболее эффективный способ чтобы сделать это. Вопрос не нужно было закрывать
abgordon

Ответы:

157

Вам нужно перебрать каждый из элементов в срезе и протестировать. Равенство для ломтиков не определено. Однако есть bytes.Equalфункция, если вы сравниваете значения типа []byte.

func testEq(a, b []Type) bool {

    // If one is nil, the other must also be nil.
    if (a == nil) != (b == nil) { 
        return false; 
    }

    if len(a) != len(b) {
        return false
    }

    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }

    return true
}
Стивен Вайнберг
источник
15
Предложение: for i, v := range a { if v != b[i] { return false } }.
zzzz
19
@zzzz Осторожно, это не удастся на разных длинах.
FiloSottile
2
Это не работает, если тип элемента не поддерживает ==. Кроме того, IIUC, Go не имеет ничего общего с дженериками. Это означает, что вы должны скопировать и вставить эту функцию для каждого типа элемента, который вы хотите поддерживать. Это очевидно то, что должно поставляться с языком. На самом деле, это так (хотя и с магией отражения), и Виктор дает ответ. Тот факт, что этот ответ выбран выше этого ответа и более высоко
оценен,
5
Go как язык имеет тенденцию рекомендовать не использовать рефлексию без крайней необходимости. Да, это должно быть сделано для каждого типа, но это обычно не то, что вы делаете в любом случае. Кроме того, reflective.DeepEqual может сделать что-то, чего вы не ожидаете, например, сказать, что два разных указателя равны, потому что значения, на которые они указывают, равны.
Стивен Вайнберг
2
@FiloSottile Длина проверяется заранее, цикл достигается только если длины различаются.
icza
259

Вы должны использовать refle.DeepEqual ()

DeepEqual - это рекурсивная релаксация оператора Go's ==.

DeepEqual сообщает, являются ли x и y «глубоко равными», определяется следующим образом. Два значения одинакового типа глубоко равны, если применяется один из следующих случаев. Значения различных типов никогда не бывают глубоко равными.

Значения массива глубоко равны, когда их соответствующие элементы глубоко равны.

Значения структуры глубоко равны, если их соответствующие поля, как экспортированные, так и не экспортированные, глубоко совпадают.

Значения Func глубоко равны, если оба равны нулю; в противном случае они не глубоко равны.

Значения интерфейса глубоко равны, если они содержат глубоко равные конкретные значения.

Значения карты глубоко равны, если они являются одним и тем же объектом карты или имеют одинаковую длину, и их соответствующие ключи (соответствующие с помощью равенства Go) отображаются на глубоко равные значения.

Значения указателя глубоко равны, если они равны с помощью оператора Go's == или если они указывают на глубоко равные значения.

Значения среза глубоко равны, когда все из следующих условий являются истинными: они оба равны нулю или оба не равны нулю, имеют одинаковую длину и либо указывают на одну и ту же начальную запись одного и того же базового массива (то есть & x [0 ] == & y [0]) или соответствующие им элементы (вплоть до длины) глубоко равны. Обратите внимание, что ненулевой пустой слайс и нулевой слайс (например, [] byte {} и [] byte (nil)) не являются глубоко равными.

Другие значения - числа, числа, строки и каналы - очень равны, если они равны с помощью оператора Go ==.

Виктор Дерягин
источник
13
Очень полезный ответ. Независимо от общего отражения производительности пакета, очень хорошо иметь предварительно упакованную функцию глубокого равенства для использования в тестовых случаях, где простота и правильность имеют первостепенное значение.
Слабый указатель
15
Я только что выполнил тест и отразил. DeepEqual в 150 раз медленнее, чем цикл. Просто к сведению, если кто-то хочет использовать этот метод в производстве.
nikdeapen
2
Он не сравнивает случайно упорядоченные фрагменты с одинаковыми элементами :(
Hemant_Negi
5
@Hemant_Negi два среза не равны, если они имеют другой порядок. Если вы хотите сравнить равенство двух срезов, игнорируя порядок, то отсортируйте их и затем проверьте или переместите элементы из одного среза на карту, а затем убедитесь, что каждый элемент на другом срезе находится на карте. (дополнительно убедитесь, что они имеют одинаковую длину)
robbert229
3
Роб Пайк (в 2011 году) размышляет в Go и пишет в официальном блоге Go: «Это мощный инструмент, который следует использовать с осторожностью и избегать, если в этом нет особой необходимости» blog.golang.org/laws-of-reflection . Я бы не использовал отражение в производственном коде только для сравнения фрагментов. Это простая функция для написания. Но обратите внимание, что в выбранном ответе на этот вопрос также есть потенциальный недостаток, в зависимости от того, какое поведение вы ожидаете от него: он обнаружит, что срезы, которые были инициализированы, но все еще находятся на len 0 и cap 0, не соответствуют срезам, которые были объявлено, но не инициализировано.
Jrefior
44

Это всего лишь пример с использованием refle.DeepEqual (), который дан в ответе @ VictorDeryagin.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := []int {4,5,6}
    b := []int {4,5,6}
    c := []int {4,5,6,7}

    fmt.Println(reflect.DeepEqual(a, b))
    fmt.Println(reflect.DeepEqual(a, c))

}

Результат:

true
false

Попробуйте в Go Playground

Akavall
источник
23

Если у вас есть два []byte, сравните их, используя bytes.Equal . Документация Голанга говорит:

Equal возвращает логическое сообщение о том, что a и b имеют одинаковую длину и содержат одинаковые байты. Нулевой аргумент эквивалентен пустому фрагменту.

Использование:

package main

import (
    "fmt"
    "bytes"
)

func main() {
    a := []byte {1,2,3}
    b := []byte {1,2,3}
    c := []byte {1,2,2}

    fmt.Println(bytes.Equal(a, b))
    fmt.Println(bytes.Equal(a, c))
}

Это напечатает

true
false
KeksArmee
источник
почему это не топ
lurf jurv
3

А пока вот https://github.com/google/go-cmp, который

предназначен для того, чтобы быть более мощной и безопасной альтернативой reflect.DeepEqualдля сравнения того, являются ли два значения семантически равными.

package main

import (
    "fmt"

    "github.com/google/go-cmp/cmp"
)

func main() {
    a := []byte{1, 2, 3}
    b := []byte{1, 2, 3}

    fmt.Println(cmp.Equal(a, b)) // true
}
lk_vc
источник
1

В случае, если вы заинтересованы в написании теста, то github.com/stretchr/testify/assertэто ваш друг.

Импортируйте библиотеку в самом начале файла:

import (
    "github.com/stretchr/testify/assert"
)

Затем внутри теста вы делаете:


func TestEquality_SomeSlice (t * testing.T) {
    a := []int{1, 2}
    b := []int{2, 1}
    assert.Equal(t, a, b)
}

Появится сообщение об ошибке:

                Diff:
                --- Expected
                +++ Actual
                @@ -1,4 +1,4 @@
                 ([]int) (len=2) {
                + (int) 1,
                  (int) 2,
                - (int) 2,
                  (int) 1,
Test:           TestEquality_SomeSlice
Габриэль Фюрстенхайм
источник
assert.Equalвнутреннее использование, reflect.DeepEqualкоторое может сделать ваши тесты работать медленнее и в конечном итоге ваш конвейер.
Дипак Сах
@DeepakSah У вас есть тесты на разницу в производительности? По моему опыту, узкое место в тестах не равнозначно, и вы получаете отличные качественные сообщения, которые повышают производительность
Габриэль Фюрстенхайм,