diff --git a/orderedmap.go b/orderedmap.go index 61587d9..7936e9e 100644 --- a/orderedmap.go +++ b/orderedmap.go @@ -76,6 +76,24 @@ func (o *OrderedMap) Delete(key string) { delete(o.values, key) } +// Range over the ordered map, accessing entries in the same order they were +// added, making the traversal deterministic. +// This API is based on the api `sync.Map.Range`. +// +// Provide a call function, `fn`, which will receive the key and value of each +// iteration. The callback function returns a bool indicating whether or not to +// continue looping. +// `true`: continue iteration +// `false`: break from interation +func (o *OrderedMap) Range(fn func(key string, value interface{}) bool) { + for _, k := range o.keys { + v := o.values[k] + if !fn(k, v) { + break + } + } +} + func (o *OrderedMap) Keys() []string { return o.keys } diff --git a/orderedmap_test.go b/orderedmap_test.go index 5b89ef6..abf0127 100644 --- a/orderedmap_test.go +++ b/orderedmap_test.go @@ -69,10 +69,10 @@ func TestOrderedMap(t *testing.T) { // Values method values := o.Values() expectedValues := map[string]interface{}{ - "number": 4, - "string": "x", + "number": 4, + "string": "x", "strings": []string{"t", "u"}, - "mixed": []interface{}{ 1, "1" }, + "mixed": []interface{}{1, "1"}, } if !reflect.DeepEqual(values, expectedValues) { t.Error("Values method returned unexpected map") @@ -595,3 +595,68 @@ func TestOrderedMap_empty_map(t *testing.T) { t.Error("Got", marshalledStr) } } + +func TestOrderedMap_Range(t *testing.T) { + tests := map[string]struct { + in []struct { + k string + v int + } + out []int + rangeFunc func(k string, v interface{}) bool + }{ + "test full range": { + in: []struct { + k string + v int + }{ + {k: "hello", v: 1}, + {k: "there", v: 2}, + {k: "outlier", v: 567}, + {k: "wow", v: 4}, + }, + out: []int{1, 2, 567, 4}, + }, + "test range with break": { + in: []struct { + k string + v int + }{ + {k: "hello", v: 1}, + {k: "there", v: 2}, + {k: "outlier", v: 567}, + {k: "wow", v: 4}, + }, + out: []int{1, 2, 567}, + rangeFunc: func(k string, v interface{}) bool { + return k != "outlier" + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + o := New() + + for _, p := range test.in { + o.Set(p.k, p.v) + } + + var out []int + o.Range(func(k string, v interface{}) bool { + i, ok := v.(int) + if !ok { + t.Errorf("%q is not an int. got=%v", k, v) + } + + out = append(out, i) + + if test.rangeFunc != nil { + return test.rangeFunc(k, v) + } + + return true + }) + }) + } +}