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:
parent
9b5cbaa2c3
commit
f98dd4129f
11
README.md
11
README.md
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"},
|
||||
|
Loading…
Reference in New Issue
Block a user