mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
tpl: Add union template func
This commit is contained in:
parent
63e2a46f63
commit
5d0748ce51
3 changed files with 116 additions and 2 deletions
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue