1

I get an array of objects with image locations via API and I can't manage to render it.

I managed to get my array into chuncks of 4 using underscore, but I can't get my head around how to massage my data into correct form so I could render it out through mustache.

The code currently looks like this:

var template = "<ul>{{#images}}<li>{{location}}</li>{{/images}}</ul>";

var data = [
    {"location":"gallery\/Baar_Toit_5.jpg"},
    {"location":"gallery\/Baar_Toit_7.jpg"},
    {"location":"gallery\/Baar_Toit_8.jpg"},
    {"location":"gallery\/Baar_Int_1.jpg"},
    {"location":"gallery\/Baar_Int_2.jpg"},
    {"location":"gallery\/Baar_Int_3.jpg"},
    {"location":"gallery\/Baar_Int_4.jpg"},
    {"location":"gallery\/Baar_Uus_01.jpg"},
    {"location":"gallery\/Baar_Uus_02.jpg"},
    {"location":"gallery\/Baar_Uus_03.jpg"},
    {"location":"gallery\/Baar_Uus_04.jpg"},
    {"location":"gallery\/Baar_Uus_05.jpg"},
    {"location":"gallery\/Baar_Uus_06.jpg"},
    {"location":"gallery\/Baar_Uus_07.jpg"}
];

var n = 4;
var imgs = _.groupBy(data, function(element, index){
    return Math.floor(index/n);
});

console.log(imgs);

Mustache.parse(template);
var rendered = Mustache.render(template, { imgs: JSON.parse(imgs) });
$('#gallery').html(rendered);

I created a small sandbox for testing purposes, feel free to play around with it: http://jsfiddle.net/qK5NT/149/

My desired output is:

<ul>
    <li>
        <p>img/1.jpg</p>
        <p>img/2.jpg</p>
        <p>img/3.jpg</p>
        <p>img/4.jpg</p>
    </li>
    <li>
        <p>img/5.jpg</p>
        <p>img/6.jpg</p>
        <p>img/5.jpg</p>
        <p>img/6.jpg</p>
    </li>
    <li>
        <p>img/5.jpg</p>
        <p>img/6.jpg</p>
        <p>img/5.jpg</p>
        <p>img/6.jpg</p>
    </li>
    <li>
        <p>img/5.jpg</p>
        <p>img/6.jpg</p>
    </li>
</ul>

Any help will be appreciated!

mu is too short
  • 426,620
  • 70
  • 833
  • 800
herrsiim
  • 137
  • 1
  • 11

1 Answers1

3

groupBy isn't the right tool for this job, objects aren't ordered in JavaScript so there's no guarantee that you'll get your chunks in the right order. Yes, this means that the canonical answer for chunking is wrong.

So your first problem is to properly chunk the data. If you're using lodash, you could use its _.chunk. If you're using plain Underscore, you could mixin your own _.chunk and the lodash version can be easily adapted:

_.mixin({
    chunk: function(a, n) {
        // Based on lodash's
        var i = 0, length = a.length, ri = -1;
        var result = Array(Math.ceil(length / n));
        while(i < length)
            result[++ri] = a.slice(i, i += n);
        return result;
    }
});

Now you can chunk your data into fours and get an array-of-arrays out with everything in the right order:

var chunks = _(data).chunk(4);

Then you want to hand the chunks to your template:

var rendered = Mustache.render(template, {
    chunks: chunks
});

And finally, your template needs to iterate over the chunks to produce the <li>s and then iterate over each chunk to produce the <p>s inside the <li>s. That would look like this:

<ul>
    {{#chunks}}
        <li>
            {{#.}}
                <p>{{location}}</p>
            {{/.}}
        </li>
    {{/chunks}}
</ul>

Demo: http://jsfiddle.net/ambiguous/b2te16pj/

Community
  • 1
  • 1
mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • Except the example calls `.toArray` on the returned object, so you will get back an ordered array. – Colin DeClue Apr 28 '15 at 21:18
  • 1
    @ColinDeClue: But `_.toArray(some_object)` doesn't guarantee that it will sort the `some_object` by its keys, `toArray` will just iterate over the object and give you the values in whatever order `_.each` would use (i.e. no specified order at all). Tacking a `toArray` call onto the `groupBy` doesn't solve the order problem. – mu is too short Apr 28 '15 at 21:22
  • @mu is too short: Thank you for a such a great answer! – herrsiim Apr 29 '15 at 12:15
  • @muistooshort: Ah, I assumed it would go off the keys in the object. But yeah that would only work if the keys were always numbers, which it can't guarantee. – Colin DeClue Apr 29 '15 at 15:00
  • @ColinDeClue: The keys are **always** strings but it doesn't matter what the keys are because nothing anywhere sorts them. The object's keys are no ordered, `toArray` uses `_.values` and `_.keys` internally (at least the current version does) and neither of those sort the keys (and they'd get it wrong if they did because the keys are strings and `'2'` and `'11'` don't sort like numbers). That other implementation is relying on accidental and unspecified behavior so it is wrong. – mu is too short Apr 29 '15 at 16:33
  • @muistooshort: Interestingly, Chrome sorts object keys numerically. (as in, it would do `1`,`2`,`3`,`11`,`foo`, etc., regardless of insertion order. So it's not accidental behavior, but it is nonguaranteed. – Colin DeClue Apr 29 '15 at 17:22
  • @ColinDeClue: I think it used to use insertion order. If they've changed then a lot of Backbone events and routing code that depended on the old undocumented behavior is now broken. That would actually be a good illustration of why assuming order in an object is a bad idea :) – mu is too short Apr 29 '15 at 17:53
  • @ColinDeClue: Actually, I think it is still using insertion order combined with some sort of "numbers first" magic (http://jsfiddle.net/ambiguous/1gccpj9L/). This is a nice illustration of the dangers of generalizing from one case or the "just try it and see what happens" school of thought :) – mu is too short Apr 29 '15 at 18:01