From 61ce7d5db841ad78784c444f9c20db32dc98ca8c Mon Sep 17 00:00:00 2001 From: Jarrah-libremfg <122067969+Jarrah-libremfg@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:30:20 +1000 Subject: [PATCH] Fix panic calling $distinct on array of arrays (#34) This fixes the following panic: ``` panic: runtime error: hash of unhashable type []interface {} goroutine 176 [running]: http://github.com/xiatechs/jsonata-go/jlib.Distinct ({0x1087cc0?, 0xc000661368?, 0x0?}) /go/pkg/mod/github.com/xiatechs/jsonata-go@v1.8.4/jlib/array.go:61 +0x3e5 reflect.Value.call({0x10c10a0?, 0x1404a18?, 0x4cadbf?}, {0x1318275, 0x4}, {0xc000661590, 0x1, 0x0?}) /usr/local/go/src/reflect/value.go:596 +0xce5 reflect.Value.Call({0x10c10a0?, 0x1404a18?, 0x1?}, {0xc000661590?, 0xc000661590?, 0x1087d00?}) /usr/local/go/src/reflect/value.go:380 +0xb9 ``` --- jlib/array.go | 10 +++++----- jsonata_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/jlib/array.go b/jlib/array.go index 8c1f734..690bc70 100644 --- a/jlib/array.go +++ b/jlib/array.go @@ -45,14 +45,14 @@ func Distinct(v reflect.Value) interface{} { for i := 0; i < items.Len(); i++ { item := jtypes.Resolve(items.Index(i)) - if jtypes.IsMap(item) { - // We can't hash a map, so convert it to a + if jtypes.IsMap(item) || jtypes.IsArray(item) { + // We can't hash a map or array, so convert it to a // string that is hashable - mapItem := fmt.Sprint(item.Interface()) - if _, ok := visited[mapItem]; ok { + unhashableItem := fmt.Sprint(item.Interface()) + if _, ok := visited[unhashableItem]; ok { continue } - visited[mapItem] = struct{}{} + visited[unhashableItem] = struct{}{} distinctValues = reflect.Append(distinctValues, item) continue diff --git a/jsonata_test.go b/jsonata_test.go index 687a8dc..2965827 100644 --- a/jsonata_test.go +++ b/jsonata_test.go @@ -8028,6 +8028,28 @@ func TestTransform(t *testing.T) { }) } +func TestUnhashableDistinct(t *testing.T) { + runTestCases(t, testdata.address, []*testCase{ + { + Expression: `$distinct([["a", "b"]])`, + Output: []interface{}{ + []interface{}{ + "a", + "b", + }, + }, + }, + { + Expression: `$distinct([{"a": "b"}])`, + Output: []interface{}{ + map[string]interface{}{ + "a": "b", + }, + }, + }, + }) +} + // Helper functions type compareFunc func(interface{}, interface{}) bool