Skip to content

Commit

Permalink
Add chunk function to split array into smaller
Browse files Browse the repository at this point in the history
  • Loading branch information
Karel Bilek committed Dec 4, 2020
1 parent a81d0aa commit e3ce235
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
10 changes: 10 additions & 0 deletions docs/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ equivalent of `list[n:m]`.
`slice` panics if there is a problem while `mustSlice` returns an error to the
template engine if there is a problem.

## chunk

To split a list into chunks of given size, use `chunk size list`. This is useful for pagination.

```
chunk 3 (list 1 2 3 4 5 6 7 8)
```

This produces list of lists `[ [ 1 2 3 ] [ 4 5 6 ] [ 7 8 ] ]`.

## A Note on List Internals

A list is implemented in Go as a `[]interface{}`. For Go developers embedding
Expand Down
2 changes: 2 additions & 0 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ var genericMap = map[string]interface{}{
"mustSlice": mustSlice,
"concat": concat,
"dig": dig,
"chunk": chunk,
"mustChunk": mustChunk,

// Crypto:
"bcrypt": bcrypt,
Expand Down
45 changes: 45 additions & 0 deletions list.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sprig

import (
"fmt"
"math"
"reflect"
"sort"
)
Expand Down Expand Up @@ -72,6 +73,50 @@ func mustPrepend(list interface{}, v interface{}) ([]interface{}, error) {
}
}

func chunk(size int, list interface{}) [][]interface{} {
l, err := mustChunk(size, list)
if err != nil {
panic(err)
}

return l
}

func mustChunk(size int, list interface{}) ([][]interface{}, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)

l := l2.Len()

cs := int(math.Floor(float64(l-1)/float64(size)) + 1)
nl := make([][]interface{}, cs)

for i := 0; i < cs; i++ {
clen := size
if i == cs-1 {
clen = int(math.Floor(math.Mod(float64(l), float64(size))))
if clen == 0 {
clen = size
}
}

nl[i] = make([]interface{}, clen)

for j := 0; j < clen; j++ {
ix := i*size + j
nl[i][j] = l2.Index(ix).Interface()
}
}

return nl, nil

default:
return nil, fmt.Errorf("Cannot chunk type %s", tp)
}
}

func last(list interface{}) interface{} {
l, err := mustLast(list)
if err != nil {
Expand Down
26 changes: 26 additions & 0 deletions list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ func TestMustPush(t *testing.T) {
}
}

func TestChunk(t *testing.T) {
tests := map[string]string{
`{{ tuple 1 2 3 4 5 6 7 | chunk 3 | len }}`: "3",
`{{ tuple | chunk 3 | len }}`: "0",
`{{ range ( tuple 1 2 3 4 5 6 7 8 9 | chunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8-9|",
`{{ range ( tuple 1 2 3 4 5 6 7 8 | chunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8|",
`{{ range ( tuple 1 2 | chunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2|",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}

func TestMustChunk(t *testing.T) {
tests := map[string]string{
`{{ tuple 1 2 3 4 5 6 7 | mustChunk 3 | len }}`: "3",
`{{ tuple | mustChunk 3 | len }}`: "0",
`{{ range ( tuple 1 2 3 4 5 6 7 8 9 | mustChunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8-9|",
`{{ range ( tuple 1 2 3 4 5 6 7 8 | mustChunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2-3|4-5-6|7-8|",
`{{ range ( tuple 1 2 | mustChunk 3 ) }}{{. | join "-"}}|{{end}}`: "1-2|",
}
for tpl, expect := range tests {
assert.NoError(t, runt(tpl, expect))
}
}

func TestPrepend(t *testing.T) {
tests := map[string]string{
`{{ $t := tuple 1 2 3 }}{{ prepend $t 0 | len }}`: "4",
Expand Down

0 comments on commit e3ce235

Please sign in to comment.