URI: 
       tpl: Add union template func - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit 5d0748ce51d0a86843bfd6569fd3cd18fa20ed5a
   DIR parent 63e2a46f630ea95b6a82ddd7af240f87026871fb
  HTML Author: digitalcraftsman <digitalcraftsman@users.noreply.github.com>
       Date:   Sun, 12 Mar 2017 23:04:12 +0100
       
       tpl: Add union template func
       
       
       Diffstat:
         M docs/content/templates/functions.md |      17 +++++++++++++++++
         M tpl/tplimpl/template_funcs.go       |      50 +++++++++++++++++++++++++++++++
         M tpl/tplimpl/template_funcs_test.go  |      51 +++++++++++++++++++++++++++++--
       
       3 files changed, 116 insertions(+), 2 deletions(-)
       ---
   DIR diff --git a/docs/content/templates/functions.md b/docs/content/templates/functions.md
       @@ -214,6 +214,23 @@ e.g.
            </ul>
        
        
       +### union
       +Given two arrays (or slices) A and B, this function will return a new array that contains the elements or objects that belong to either A or to B or to both. The elements supported are strings, integers and floats (only float64).
       +
       +```
       +{{ union (slice 1 2 3) (slice 3 4 5) }}
       +<!-- returns [1 2 3 4 5] -->
       +
       +{{ union (slice 1 2 3) nil }}
       +<!-- returns [1 2 3] -->
       +
       +{{ union nil (slice 1 2 3) }}
       +<!-- returns [1 2 3] -->
       +
       +{{ union nil nil }}
       +<!-- returns an error because both arrays/slices have to be of the same type -->
       +```
       +
        ### isset
        Returns true if the parameter is set.
        Takes either a slice, array or channel and an index or a map and a key as input.
   DIR diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
       @@ -399,6 +399,55 @@ func intersect(l1, l2 interface{}) (interface{}, error) {
                }
        }
        
       +// union returns the union of the given sets, l1 and l2. l1 and
       +// l2 must be of the same type and may be either arrays or slices.
       +// If l1 and l2 aren't of the same type then l1 will be returned.
       +// If either l1 or l2 is nil then the non-nil list will be returned.
       +func union(l1, l2 interface{}) (interface{}, error) {
       +        if l1 == nil && l2 == nil {
       +                return nil, errors.New("both arrays/slices have to be of the same type")
       +        } else if l1 == nil && l2 != nil {
       +                return l2, nil
       +        } else if l1 != nil && l2 == nil {
       +                return l1, nil
       +        }
       +
       +        l1v := reflect.ValueOf(l1)
       +        l2v := reflect.ValueOf(l2)
       +
       +        switch l1v.Kind() {
       +        case reflect.Array, reflect.Slice:
       +                switch l2v.Kind() {
       +                case reflect.Array, reflect.Slice:
       +                        r := reflect.MakeSlice(l1v.Type(), 0, 0)
       +
       +                        if l1v.Type() != l2v.Type() {
       +                                return r.Interface(), nil
       +                        }
       +
       +                        for i := 0; i < l1v.Len(); i++ {
       +                                elem := l1v.Index(i)
       +                                if !in(r.Interface(), elem.Interface()) {
       +                                        r = reflect.Append(r, elem)
       +                                }
       +                        }
       +
       +                        for j := 0; j < l2v.Len(); j++ {
       +                                elem := l2v.Index(j)
       +                                if !in(r.Interface(), elem.Interface()) {
       +                                        r = reflect.Append(r, elem)
       +                                }
       +                        }
       +
       +                        return r.Interface(), nil
       +                default:
       +                        return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
       +                }
       +        default:
       +                return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
       +        }
       +}
       +
        type imageHandler struct {
                imageConfigCache map[string]image.Config
                sync.RWMutex
       @@ -2193,6 +2242,7 @@ func (t *templateFuncster) initFuncMap() {
                        "time":         asTime,
                        "trim":         trim,
                        "truncate":     truncate,
       +                "union":        union,
                        "upper":        upper,
                        "urlize":       t.PathSpec.URLize,
                        "where":        where,
   DIR diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go
       @@ -184,6 +184,7 @@ trim: {{ trim "++Batman--" "+-" }}
        truncate: {{ "this is a very long text" | truncate 10 " ..." }}
        truncate: {{ "With [Markdown](/markdown) inside." | markdownify | truncate 14 }}
        upper: {{upper "BatMan"}}
       +union: {{ union (slice 1 2 3) (slice 3 4 5) }}
        urlize: {{ "Bat Man" | urlize }}
        `
        
       @@ -260,6 +261,7 @@ trim: Batman
        truncate: this is a ...
        truncate: With <a href="/markdown">Markdown …</a>
        upper: BATMAN
       +union: [1 2 3 4 5]
        urlize: bat-man
        `
        
       @@ -988,8 +990,6 @@ func TestIntersect(t *testing.T) {
                        {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},
                        {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
                        {[]string{}, []string{}, []string{}},
       -                {[]string{"a", "b"}, nil, make([]interface{}, 0)},
       -                {nil, []string{"a", "b"}, make([]interface{}, 0)},
                        {nil, nil, make([]interface{}, 0)},
                        {[]string{"1", "2"}, []int{1, 2}, []string{}},
                        {[]int{1, 2}, []string{"1", "2"}, []int{}},
       @@ -1021,6 +1021,53 @@ func TestIntersect(t *testing.T) {
                }
        }
        
       +func TestUnion(t *testing.T) {
       +        t.Parallel()
       +        for i, this := range []struct {
       +                sequence1 interface{}
       +                sequence2 interface{}
       +                expect    interface{}
       +                isErr     bool
       +        }{
       +                {[]string{"a", "b", "c", "c"}, []string{"a", "b", "b"}, []string{"a", "b", "c"}, false},
       +                {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b", "c"}, false},
       +                {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{"a", "b", "c", "d", "e"}, false},
       +                {[]string{}, []string{}, []string{}, false},
       +                {[]string{"a", "b"}, nil, []string{"a", "b"}, false},
       +                {nil, []string{"a", "b"}, []string{"a", "b"}, false},
       +                {nil, nil, make([]interface{}, 0), true},
       +                {[]string{"1", "2"}, []int{1, 2}, make([]string, 0), false},
       +                {[]int{1, 2}, []string{"1", "2"}, make([]int, 0), false},
       +                {[]int{1, 2, 3}, []int{3, 4, 5}, []int{1, 2, 3, 4, 5}, false},
       +                {[]int{1, 2, 3}, []int{1, 2, 3}, []int{1, 2, 3}, false},
       +                {[]int{1, 2, 4}, []int{2, 4}, []int{1, 2, 4}, false},
       +                {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4, 1}, false},
       +                {[]int{1, 2, 4}, []int{3, 6}, []int{1, 2, 4, 3, 6}, false},
       +                {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4, 1.1}, false},
       +        } {
       +                results, err := union(this.sequence1, this.sequence2)
       +                if err != nil && !this.isErr {
       +                        t.Errorf("[%d] failed: %s", i, err)
       +                        continue
       +                }
       +                if !reflect.DeepEqual(results, this.expect) && !this.isErr {
       +                        t.Errorf("[%d] got %v but expected %v", i, results, this.expect)
       +                }
       +        }
       +
       +        _, err1 := union("not an array or slice", []string{"a"})
       +
       +        if err1 == nil {
       +                t.Error("Expected error for non array as first arg")
       +        }
       +
       +        _, err2 := union([]string{"a"}, "not an array or slice")
       +
       +        if err2 == nil {
       +                t.Error("Expected error for non array as second arg")
       +        }
       +}
       +
        func TestIsSet(t *testing.T) {
                t.Parallel()