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

Handling of maps in for-loops #125

Closed
hartfelt opened this issue Mar 28, 2019 · 4 comments
Closed

Handling of maps in for-loops #125

hartfelt opened this issue Mar 28, 2019 · 4 comments
Assignees

Comments

@hartfelt
Copy link
Contributor

Hello,

According to https://github.com/shopify/liquid/wiki/Liquid-for-Designers it should be possible to iterate a map with a forloop, like:

{% for item in hash %}
    {{ item[0] }}: {{ item[1] }}
{% endfor %}

I don't see any mention of this on https://shopify.github.io/liquid/tags/iteration/ though, but a quick test show that it is indeed supported by the ruby version:

$ cat test.rb 
require 'liquid'
@template = Liquid::Template.parse("{% for item in hash %}{{ item[0] }} is {{ item[1] }};{% endfor %}");
puts @template.render('hash' => {
	'a' => 'AAA',
	'b' => 'BBB'})
$ ruby test.rb 
a is AAA;b is BBB;

However, the current handling in liqp is to wrap the entire map in a list, resulting in one iteration where item is equals to hash. This behaviour is a result of asArray(Object value) -> Object[] in LValue, and is indeed documented in the comment there. Is this difference from the ruby version intentional or just an oversight?

As an experiment, in the codebase where I'm using liqp, I've copied the for-tag in order to add the following (quite hackish) case to asArray:

if (value instanceof Map) {
	return ((List<List<Object>>)((((Map)value).entrySet().stream().map(e -> Arrays.asList(
		((Map.Entry<Object, Object>)e).getKey(),
		((Map.Entry<Object, Object>)e).getValue())).collect(Collectors.toList())))).toArray();
}

...which makes the following test pass:

@Test
public void testLoopingMap() {
	Map<String, Object> inner = new HashMap<>();
	inner.put("a", "AAA");
	inner.put("b", "BBB");
	Map<String, Object> outer = new HashMap<>();
	outer.put("hash", inner);
	
	String o = Template.parse(
		"{% for item in hash %}" +
			"{{ item[0] }} is {{ item[1] }};" +
		"{% endfor %}").with(new HackedForTag()).render(outer);
	assertEquals("a is AAA;b is BBB;", o);
}

If the current behaviour is indeed an oversight, I'll work on cleaning up the above fix and submit it as a merge request, along with some tests.

Or, if some other approach is preferred, feel free to point me in the right direction ;)

--
Bjørn Hartfelt

@bkiers
Copy link
Owner

bkiers commented Mar 28, 2019

@hartfelt thanks for your detailed report. I will get back to you this weekend.

@bkiers bkiers self-assigned this Mar 28, 2019
@hartfelt
Copy link
Contributor Author

@bkiers Thanks for the quick reply. I'll look forward to your further comments :)

@bkiers
Copy link
Owner

bkiers commented Mar 29, 2019

Thanks, your solution was spot on. I only needed to unwrap the stream/collect calls since it (still) needs to be Java 7 compliant.

@hartfelt
Copy link
Contributor Author

hartfelt commented Apr 1, 2019

Great! Thanks for the quick resolution 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants