1

In short, I want to create a generic method "organize" to turn this:

[
    { name: 'band1', type: 'concert', subtype: 'rock' },
    { name: 'band2', type: 'concert', subtype: 'jazz' },
    { name: 'band3', type: 'concert', subtype: 'jazz' },
    { name: 'fun1', type: 'festival', subtype: 'beer' },
    { name: 'fun2', type: 'festival', subtype: 'food' },
]

into this:

{
    'concert': {
      'rock': [
        { name: 'band1', type: 'concert', subtype: 'rock' },
      ],
      'jazz': [
        { name: 'band2', type: 'concert', subtype: 'jazz' },
        { name: 'band3', type: 'concert', subtype: 'jazz' }
      ]
    },
    'festival': {
      'beer': [
        { name: 'fun1', type: 'festival', subtype: 'beer' },
      ],
      'food': [
        { name: 'fun2', type: 'festival', subtype: 'food' },
      ]
    }
}

from the signature:

organize(events, ['type', 'subtype']);

The reason I want to do this is to have a simple nested for loop to display the data. If you're familiar with Vue.js, it'll look like this:

<div v-for="(type, subtypes) in organize(events, ['type', 'subtype'])">
  <h1>{{ type }}</h1>
  <div v-for="(subtype, events) in subtypes">
    <h2>{{ subtype }}</h2>
    <div v-for="event in events">
      <h3>{{ event.name }}</h3>
    </div>
  </div>
</div>

For me it's been a brain-bending exercise. I think it's ripe for recursion and some combination of map filter reduce, but I've spent too much time trying to think of a solution, and instead wrote some very ugly code to accomplish essentially the same thing. It's also possible that I'm not aware of some useful feature of javascript which could help me do this....

Advice or guidance would be appreciated, but it's not a pressing matter. I just think it'd be really useful to have a generic method to organize arrays as such.

1
  • Just a suggestion: if you already have a solution that works, but you think can be further optimised, you can post a question over at CodeReview ;) Commented Oct 1, 2016 at 22:41

2 Answers 2

2

I think this could be a correct implementation of organize:

function organize(rows, groupBy) {
    var last = groupBy.length - 1;
    return rows.reduce ( (res, obj) => {
        groupBy.reduce( (res, grp, i) => 
            res[obj[grp]] || (res[obj[grp]] = i == last ? [] : {}), res).push(obj);
        return res;
    }, {});
}

var events = [
    { name: 'band1', type: 'concert', subtype: 'rock' },
    { name: 'band2', type: 'concert', subtype: 'jazz' },
    { name: 'band3', type: 'concert', subtype: 'jazz' },
    { name: 'fun1', type: 'festival', subtype: 'beer' },
    { name: 'fun2', type: 'festival', subtype: 'food' },
];

var res = organize(events, ['type','subtype']);

console.log(res);

Sign up to request clarification or add additional context in comments.

1 Comment

nicely done! I don't have time to look over it tonight, but copying and pasting your function into my code works like a charm! Much cleaner. I'm excited to go over it later. I had a hunch reduce would be useful, but I always overlook it in favor of map and filter. Thanks!
0

You could use _.groupBy() from the LoDash library:

var source = [
  { name: 'band1', type: 'concert', subtype: 'rock' },
  { name: 'band2', type: 'concert', subtype: 'jazz' },
  { name: 'band3', type: 'concert', subtype: 'jazz' },
  { name: 'fun1', type: 'festival', subtype: 'beer' },
  { name: 'fun2', type: 'festival', subtype: 'food' },
];
  
var groupedByType = _.groupBy(source, 'type');
console.log(groupedByType);
  
var groupedByTypeAndSubtype = {};
for (var i in groupedByType) {
  groupedByTypeAndSubtype[i] = _.groupBy(groupedByType[i], 'subtype');
}

console.log(groupedByTypeAndSubtype);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.2/lodash.js"></script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.