From e28d9aa42c3429d22fe254e69e4605aaf1e684f3 Mon Sep 17 00:00:00 2001 From: Nathan Sharfi Date: Mon, 29 May 2017 12:03:38 -0700 Subject: [PATCH] tpl: Add uniq function --- docs/content/templates/functions.md | 9 +++++++ tpl/collections/collections.go | 38 +++++++++++++++++++++++++++++ tpl/collections/collections_test.go | 36 +++++++++++++++++++++++++++ tpl/collections/init.go | 6 +++++ 4 files changed, 89 insertions(+) diff --git a/docs/content/templates/functions.md b/docs/content/templates/functions.md index c514a4ec84c..a1cf9e27b70 100644 --- a/docs/content/templates/functions.md +++ b/docs/content/templates/functions.md @@ -373,6 +373,15 @@ e.g. {{ .Content }} {{ end }} + +### uniq + +Takes in a slice or array and returns a slice with subsequent duplicate elements removed. + + {{ uniq (slice 1 2 3 2) }} + {{ slice 1 2 3 2 | uniq }} + + ## Files ### readDir diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index a6331edfe0c..b025c76704f 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -587,3 +587,41 @@ func (ns *Namespace) Union(l1, l2 interface{}) (interface{}, error) { return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String()) } } + +// Uniq takes in a slice or array and returns a slice with subsequent +// duplicate elements removed. +func (ns *Namespace) Uniq(l interface{}) (interface{}, error) { + if l == nil { + return make([]interface{}, 0), nil + } + + lv := reflect.ValueOf(l) + lv, isNil := indirect(lv) + if isNil { + return nil, errors.New("invalid nil argument to Uniq") + } + + var ret reflect.Value + + switch lv.Kind() { + case reflect.Slice: + ret = reflect.MakeSlice(lv.Type(), 0, 0) + case reflect.Array: + ret = reflect.MakeSlice(reflect.SliceOf(lv.Type().Elem()), 0, 0) + default: + return nil, errors.New("Can't use Uniq on " + reflect.ValueOf(lv).Type().String()) + } + + for i := 0; i != lv.Len(); i++ { + lvv := lv.Index(i) + lvv, isNil := indirect(lvv) + if isNil { + continue + } + + if !ns.In(ret.Interface(), lvv.Interface()) { + ret = reflect.Append(ret, lvv) + } + } + return ret.Interface(), nil +} diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index 6a3d7b9e477..9943f439e9b 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -603,6 +603,42 @@ func TestUnion(t *testing.T) { } } +func TestUniq(t *testing.T) { + t.Parallel() + + ns := New(&deps.Deps{}) + for i, test := range []struct { + l interface{} + expect interface{} + isErr bool + }{ + {[]string{"a", "b", "c"}, []string{"a", "b", "c"}, false}, + {[]string{"a", "b", "c", "c"}, []string{"a", "b", "c"}, false}, + {[]string{"a", "b", "b", "c"}, []string{"a", "b", "c"}, false}, + {[]string{"a", "b", "c", "b"}, []string{"a", "b", "c"}, false}, + {[]int{1, 2, 3}, []int{1, 2, 3}, false}, + {[]int{1, 2, 3, 3}, []int{1, 2, 3}, false}, + {[]int{1, 2, 2, 3}, []int{1, 2, 3}, false}, + {[]int{1, 2, 3, 2}, []int{1, 2, 3}, false}, + {[4]int{1, 2, 3, 2}, []int{1, 2, 3}, false}, + {nil, make([]interface{}, 0), false}, + // should-errors + {1, 1, true}, + {"foo", "fo", true}, + } { + errMsg := fmt.Sprintf("[%d] %v", i, test) + + result, err := ns.Uniq(test.l) + if test.isErr { + assert.Error(t, err, errMsg) + continue + } + + assert.NoError(t, err, errMsg) + assert.Equal(t, test.expect, result, errMsg) + } +} + func (x *TstX) TstRp() string { return "r" + x.A } diff --git a/tpl/collections/init.go b/tpl/collections/init.go index fa786684bd2..25ef64e1a11 100644 --- a/tpl/collections/init.go +++ b/tpl/collections/init.go @@ -137,6 +137,12 @@ func init() { {`{{ seq 3 }}`, `[1 2 3]`}, }, ) + ns.AddMethodMapping(ctx.Uniq, + []string{"uniq"}, + [][2]string{ + {`{{ slice 1 2 3 2 | uniq }}`, `[1 2 3]`}, + }, + ) return ns