Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add group_by liquid filter #2572

Merged
merged 2 commits into from
Aug 1, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions app/concerns/liquid_interpolatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,29 @@ def as_object(object)
throw :as_object, object.as_json
end

# Group an array of items by a property
#
# Example usage:
#
# {% assign posts_by_author = site.posts | group_by: "author" %}
# {% for author in posts_by_author %}
# <dt>{{author.name}}</dt>
# {% for post in author.items %}
# <dd><a href="{{post.url}}">{{post.title}}</a></dd>
# {% endfor %}
# {% endfor %}
def group_by(input, property)
if input.respond_to?(:group_by)
[].tap do |grouped|
input.group_by { |item| item[property] }.each do |value, items|
elsurudo marked this conversation as resolved.
Show resolved Hide resolved
grouped << { 'name' => value, 'items' => items }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not absolutely sure if name/items are the best choices here. key/values, maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that jekyll which has the same filter (although they have shared code between filters, thus I simplified it here and provided a simpler implementation).

Thus, I decided to use the same key names, since that is quite a popular project and thus provides some sort of "standard". I agree that it's a bit strange, but it makes sense when you think of it as "name of the group" and "items in the group". Liquid is meant for somewhat non-technical people and designers, after all.

But in the end, I'm not tied to anything. It's a judgement-call.

end
end
else
input
end
end

private

def logger
Expand Down
42 changes: 39 additions & 3 deletions spec/concerns/liquid_interpolatable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,19 @@ def @filter.to_xpath_roundtrip(string)
@agent.interpolation_context['s'] = 'http://example.com/dir/1?q=test'
end

it 'should parse an abosule URI' do
it 'should parse an absolute URI' do
expect(@filter.to_uri('http://example.net/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.net/index.html'))
end

it 'should parse an abosule URI with a base URI specified' do
it 'should parse an absolute URI with a base URI specified' do
expect(@filter.to_uri('http://example.net/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.net/index.html'))
end

it 'should parse a relative URI with a base URI specified' do
expect(@filter.to_uri('foo/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.com/dir/foo/index.html'))
end

it 'should parse an abosule URI with a base URI specified' do
it 'should parse an absolute URI with a base URI specified' do
expect(@filter.to_uri('http://example.net/index.html', 'http://example.com/dir/1')).to eq(URI('http://example.net/index.html'))
end

Expand Down Expand Up @@ -394,4 +394,40 @@ def ensure_safety(obj)
expect(agent.interpolated['template']).to eq('38b98bc2625a8cac33369f6204e784482be5e172b242699406270856a841d1ec')
end
end

describe 'group_by' do
let(:events) do
[
{ "date" => "2019-07-30", "type" => "Snap" },
{ "date" => "2019-07-30", "type" => "Crackle" },
{ "date" => "2019-07-29", "type" => "Pop" },
{ "date" => "2019-07-29", "type" => "Bam" },
{ "date" => "2019-07-29", "type" => "Pow" },
]
end

it "should group an enumerable by the given attribute" do
expect(@filter.group_by(events, "date")).to eq(
[
{
"name" => "2019-07-30", "items" => [
{ "date" => "2019-07-30", "type" => "Snap" },
{ "date" => "2019-07-30", "type" => "Crackle" }
]
},
{
"name" => "2019-07-29", "items" => [
{ "date" => "2019-07-29", "type" => "Pop" },
{ "date" => "2019-07-29", "type" => "Bam" },
{ "date" => "2019-07-29", "type" => "Pow" }
]
}
]
)
end

it "should leave non-groupables alone" do
expect(@filter.group_by("some string", "anything")).to eq("some string")
end
end
end