1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-30 06:59:04 +01:00

Complete #9 group_by_exp

This commit is contained in:
Oliver Steele 2017-07-13 12:56:38 -04:00
parent 9b5cbaa2c3
commit f98dd4129f
3 changed files with 48 additions and 25 deletions

View File

@ -61,10 +61,6 @@ This project is at an early stage of development.
It works on the Google Pages sites that I care about, and it looks credible on a spot-check of other Jekyll sites.
It doesn't run on Windows, and it may not work for you.
In addition to the limitations listed below, this software isn't robust. Jekyll, Hugo, and other mature projects have lots of test coverage, and have had lots of testing by lots of people. I've this used only in limited ways, in the month since I started writing it.
### Current Limitations
Missing features:
@ -74,11 +70,12 @@ Missing features:
- Pagination
- Math
- CSV and JSON data files
- Plugins. (Some plugins are [emulated](./docs/plugins.md).)
- Template filters `group_by_exp` and `scssify`
- More Liquid tags and filters, listed [here](https://github.com/osteele/liquid#differences-from-liquid).
- Most plugins. (Some plugins are [emulated](./docs/plugins.md).)
- Liquid tag `tablerow`.
- Liquid filter `scssify`
- Markdown features: [attribute lists](https://kramdown.gettalong.org/syntax.html#attribute-list-definitions), [automatic ids](https://kramdown.gettalong.org/converter/html.html#auto-ids), [`markdown=1`](https://kramdown.gettalong.org/syntax.html#html-blocks).
- `site.data` is not sorted.
- Undefined Liquid tags and filters are errors, not warnings.
- Windows compatibility
Also see the [detailed status](#feature-status) below.

View File

@ -26,16 +26,16 @@ func AddJekyllFilters(e *liquid.Engine, c *config.Config) {
e.RegisterFilter("array_to_sentence_string", arrayToSentenceStringFilter)
// TODO doc neither Liquid nor Jekyll docs this, but it appears to be present
e.RegisterFilter("filter", func(values []map[string]interface{}, key string) []interface{} {
out := []interface{}{}
result := []interface{}{}
for _, value := range values {
if _, ok := value[key]; ok {
out = append(out, value)
result = append(result, value)
}
}
return out
return result
})
e.RegisterFilter("group_by", groupByFilter)
e.RegisterFilter("group_by_exp", unimplementedFilter("group_by_exp"))
e.RegisterFilter("group_by_exp", groupByExpFilter)
e.RegisterFilter("sample", func(array []interface{}) interface{} {
if len(array) == 0 {
return nil
@ -180,6 +180,31 @@ func arrayToSentenceStringFilter(array []string, conjunction func(string) string
}
}
func groupByExpFilter(array []map[string]interface{}, name string, expr expression.Closure) ([]map[string]interface{}, error) {
rt := reflect.ValueOf(array)
if !(rt.Kind() != reflect.Array || rt.Kind() == reflect.Slice) {
return nil, nil
}
groups := map[interface{}][]interface{}{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i).Interface()
key, err := expr.Bind(name, item).Evaluate()
if err != nil {
return nil, err
}
if group, found := groups[key]; found {
groups[key] = append(group, item)
} else {
groups[key] = []interface{}{item}
}
}
result := []map[string]interface{}{}
for k, v := range groups {
result = append(result, map[string]interface{}{"name": k, "items": v})
}
return result, nil
}
func groupByFilter(array []map[string]interface{}, property string) []map[string]interface{} {
rt := reflect.ValueOf(array)
if !(rt.Kind() != reflect.Array || rt.Kind() == reflect.Slice) {
@ -200,24 +225,24 @@ func groupByFilter(array []map[string]interface{}, property string) []map[string
}
}
}
out := []map[string]interface{}{}
result := []map[string]interface{}{}
for k, v := range groups {
out = append(out, map[string]interface{}{"name": k, "items": v})
result = append(result, map[string]interface{}{"name": k, "items": v})
}
return out
return result
}
func sortFilter(array []interface{}, key interface{}, nilFirst func(bool) bool) []interface{} {
nf := nilFirst(true)
out := make([]interface{}, len(array))
copy(out, array)
result := make([]interface{}, len(array))
copy(result, array)
if key == nil {
evaluator.Sort(out)
evaluator.Sort(result)
} else {
// TODO error if key is not a string
evaluator.SortByProperty(out, key.(string), nf)
evaluator.SortByProperty(result, key.(string), nf)
}
return out
return result
}
func whereExpFilter(array []interface{}, name string, expr expression.Closure) ([]interface{}, error) {
@ -225,7 +250,7 @@ func whereExpFilter(array []interface{}, name string, expr expression.Closure) (
if rt.Kind() != reflect.Array && rt.Kind() != reflect.Slice {
return nil, nil
}
out := []interface{}{}
result := []interface{}{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i).Interface()
value, err := expr.Bind(name, item).Evaluate()
@ -233,10 +258,10 @@ func whereExpFilter(array []interface{}, name string, expr expression.Closure) (
return nil, err
}
if value != nil && value != false {
out = append(out, item)
result = append(result, item)
}
}
return out, nil
return result, nil
}
func whereFilter(array []map[string]interface{}, key string, value interface{}) []interface{} {
@ -244,15 +269,15 @@ func whereFilter(array []map[string]interface{}, key string, value interface{})
if rt.Kind() != reflect.Array && rt.Kind() != reflect.Slice {
return nil
}
out := []interface{}{}
result := []interface{}{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i)
if item.Kind() == reflect.Map && item.Type().Key().Kind() == reflect.String {
attr := item.MapIndex(reflect.ValueOf(key))
if attr.IsValid() && fmt.Sprint(attr) == value {
out = append(out, item.Interface())
result = append(result, item.Interface())
}
}
}
return out
return result
}

View File

@ -25,6 +25,7 @@ var filterTests = []struct{ in, expected string }{
{`{{ array | sample }}`, "third"},
{`{{ site.members | group_by: "graduation_year" | map: "name" | sort | join }}`, "2013, 2014, 2015"},
{`{{ site.members | group_by_exp: "item", "item.graduation_year" | size }}`, "4"},
// TODO what is the default for nil first?
{`{{ animals | sort | join: ", " }}`, "Sally Snake, giraffe, octopus, zebra"},