mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	tpl: Add union template func
This commit is contained in:
		
				
					committed by
					
						
						Bjørn Erik Pedersen
					
				
			
			
				
	
			
			
			
						parent
						
							63e2a46f63
						
					
				
				
					commit
					5d0748ce51
				
			@@ -214,6 +214,23 @@ e.g.
 | 
				
			|||||||
    </ul>
 | 
					    </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
 | 
					### isset
 | 
				
			||||||
Returns true if the parameter is set.
 | 
					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.
 | 
					Takes either a slice, array or channel and an index or a map and a key as input.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
					type imageHandler struct {
 | 
				
			||||||
	imageConfigCache map[string]image.Config
 | 
						imageConfigCache map[string]image.Config
 | 
				
			||||||
	sync.RWMutex
 | 
						sync.RWMutex
 | 
				
			||||||
@@ -2193,6 +2242,7 @@ func (t *templateFuncster) initFuncMap() {
 | 
				
			|||||||
		"time":         asTime,
 | 
							"time":         asTime,
 | 
				
			||||||
		"trim":         trim,
 | 
							"trim":         trim,
 | 
				
			||||||
		"truncate":     truncate,
 | 
							"truncate":     truncate,
 | 
				
			||||||
 | 
							"union":        union,
 | 
				
			||||||
		"upper":        upper,
 | 
							"upper":        upper,
 | 
				
			||||||
		"urlize":       t.PathSpec.URLize,
 | 
							"urlize":       t.PathSpec.URLize,
 | 
				
			||||||
		"where":        where,
 | 
							"where":        where,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -184,6 +184,7 @@ trim: {{ trim "++Batman--" "+-" }}
 | 
				
			|||||||
truncate: {{ "this is a very long text" | truncate 10 " ..." }}
 | 
					truncate: {{ "this is a very long text" | truncate 10 " ..." }}
 | 
				
			||||||
truncate: {{ "With [Markdown](/markdown) inside." | markdownify | truncate 14 }}
 | 
					truncate: {{ "With [Markdown](/markdown) inside." | markdownify | truncate 14 }}
 | 
				
			||||||
upper: {{upper "BatMan"}}
 | 
					upper: {{upper "BatMan"}}
 | 
				
			||||||
 | 
					union: {{ union (slice 1 2 3) (slice 3 4 5) }}
 | 
				
			||||||
urlize: {{ "Bat Man" | urlize }}
 | 
					urlize: {{ "Bat Man" | urlize }}
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -260,6 +261,7 @@ trim: Batman
 | 
				
			|||||||
truncate: this is a ...
 | 
					truncate: this is a ...
 | 
				
			||||||
truncate: With <a href="/markdown">Markdown …</a>
 | 
					truncate: With <a href="/markdown">Markdown …</a>
 | 
				
			||||||
upper: BATMAN
 | 
					upper: BATMAN
 | 
				
			||||||
 | 
					union: [1 2 3 4 5]
 | 
				
			||||||
urlize: bat-man
 | 
					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"}, []string{"a", "b", "c"}, []string{"a", "b"}},
 | 
				
			||||||
		{[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
 | 
							{[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
 | 
				
			||||||
		{[]string{}, []string{}, []string{}},
 | 
							{[]string{}, []string{}, []string{}},
 | 
				
			||||||
		{[]string{"a", "b"}, nil, make([]interface{}, 0)},
 | 
					 | 
				
			||||||
		{nil, []string{"a", "b"}, make([]interface{}, 0)},
 | 
					 | 
				
			||||||
		{nil, nil, make([]interface{}, 0)},
 | 
							{nil, nil, make([]interface{}, 0)},
 | 
				
			||||||
		{[]string{"1", "2"}, []int{1, 2}, []string{}},
 | 
							{[]string{"1", "2"}, []int{1, 2}, []string{}},
 | 
				
			||||||
		{[]int{1, 2}, []string{"1", "2"}, []int{}},
 | 
							{[]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) {
 | 
					func TestIsSet(t *testing.T) {
 | 
				
			||||||
	t.Parallel()
 | 
						t.Parallel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user