r3labs / diff

A library for diffing golang structures
Mozilla Public License 2.0
895 stars 84 forks source link

Slice ordering can't be disabled for []map[string]interface{} #71

Open amanenk opened 2 years ago

amanenk commented 2 years ago

I am trying to compare slice of maps but the results are inconsistent. As I understand correctly the code below should show the same result for a == b and a == c

package main

import (
    "fmt"
    diff "github.com/r3labs/diff/v2"
)

func main() {
    a := []map[string]interface{}{
        {
            "test": 2,
            "a":    "test",
        },
        {
            "test": 12,
        },
    }
    b := []map[string]interface{}{
        {
            "test": 2,
            "a":    "test1",
        },
        {
            "test": 12,
        },
    }
    c := []map[string]interface{}{
        {
            "test": 12,
        },
        {
            "test": 2,
            "a":    "test1",
        },
    }
    changelog1, _ := diff.Diff(a, b, diff.StructMapKeySupport(), diff.SliceOrdering(false))
    changelog2, _ := diff.Diff(a, c, diff.StructMapKeySupport(), diff.SliceOrdering(false))
    fmt.Println(changelog1)
    fmt.Println(changelog2)
}

That would be awesome to ad an option that allows to have consistent results in this case

purehyperbole commented 2 years ago

Hey @amanenk,

Sorry for the delay in getting back to you and thanks for raising the issue!

This is a bit of a tricky one...

When diffing two slices with SliceOrdering set to false, we:

  1. loop over each element in slice a and check if it has an exact match in slice b
  2. loop over each element in slice b and check if it has an exact match in slice a
  3. add any element that does not have an exact equivalent in the other slice to a list
  4. diff the list of missing elements by order, as a fallback

Step 4 in this case is why changing the order of the slices in your example produces different results.

This works well for simple values like int or string where comparisons are binary; values match or they don't.

With more complicated values like maps, it's possible to update one of many values in the map. At this point it's ambiguous as to which maps in each slice are equivalent.

It might be possible to try to find the best possible partial match in each slice, but I'm not confident that would actually produce more reliable results.