0

Just stumbled across this scenario in some code I'm writing and was curious what the "right" method is. Say for example I have a large array of objects which I need to group by a certain property, but then the order of the groups matter.

Ex. Obj :

var obj = {
    "groupName" : ["Group1", "Group2" ]
}

So if I have a bunch of these objects in an array, it's annoying to iterate over each object, iterate over it's groupName property, check another array like "groups" to see if the group exists, if not create it, and add the object.

Does it make sense to have an object called groups with properties matching the group names, group the objects that way, and then at the end turn groups into an array (naturally using some sort of precedence field in the group to order).

ex.

var groups = {
        "Group1": {
            "precedence":1,
            "objArray": []
         }
 }

Then you can group like so, do a forEach on array of objs, forEach on obj.groupnames, if groupname in groups push obj, else groups.groupname is a new group, get precedence from a lookup table, push obj to this group.

At the end, enumerate all props of groups and add to groupsArray, then sort by precedence.

Is this more/less efficient than just using arrays? Makes less sense? Is there a better way to do this? I got it working with the above method, I was just curious from more of a theoretical sense whether this is common practice vs. inefficient and bulky :P.

Thanks!

EDIT:

A start/end result to better explain things:

   var obj1 = {
        "groupsIBelongTo" : ["A", "B"];
    };

  var obj2 = {
    "groupsIBelongTo" : ["B", "C"];
  };

  var obj3 = {
    "groupsIBelongTo" : ["A", "C"];
  };


var objArray = [ obj1, obj2, obj3];

I want to create an array of groups that contain the appropriate objs. Assume the precedence is B, A, C , the end result should be as such:

var groups = [ {
    "groupName" : "B",
    "objs" : [obj1, obj2]
    }, 

    {
      "groupName" : "A",
      "objs" : [obj1, obj3]
    }, 

    {
      "groupName" : "C",
      "objs" : [obj2, obj3]
    }, 
  ]
SleepyProgrammer
  • 443
  • 1
  • 5
  • 15
  • 1
    Using `lodash` or `underscore` or `lazy.js` would help in this situation. They have a bunch of utility functions to aide in this kind of use case. – Pete Mar 06 '15 at 14:59
  • 1
    Not sure if this post does what you want: http://stackoverflow.com/questions/28867721/how-to-sort-an-array-of-objects-using-a-related-property-from-objects-in-second/28888003#28888003 – Pete Mar 06 '15 at 15:00
  • Thanks for the response, I thought about lodash. However this is for a client who doesn't want dependencies so I was wondering about the smartest way to do it in vanilla JS. – SleepyProgrammer Mar 06 '15 at 15:01
  • Can you provide more test data? I think you can do this with `map`, `reduce`, and `sort` available in vanilla JS. You won't need to write a single loop to do this, but I need to see some test scenarios to verify this. – Pete Mar 06 '15 at 15:04
  • 1
    i.e. can you provide an example of the input data and an example of the output data. – Pete Mar 06 '15 at 15:04
  • Sure, I will edit the post shortly! – SleepyProgrammer Mar 06 '15 at 15:05
  • 1
    Depending on the size of data and the approach you're using you'd probably be better off keeping the groups as direct children (unordered) and then have an array that just defines the order of the groups. Accessing by name will be much faster than iterating over a list, and then getting the ordered version would just be iterating over the ordered array and then doing a lookup based on the value in the array. Alternately you could use an index style object which maps names to array indexes. If the data is large enough one of these would help, if it isn't then don't spend time on it. – Matt Whipple Mar 06 '15 at 15:36

1 Answers1

1

I recommend reading the array pages on the Mozilla Developer Network. They provide a wealth of knowledge and examples. This is a fairly complex problem, so it broken down into steps for clarity. Here's the demo.

Use map, reduce, filter

Step 1: Build an index of objects

var objArray = [obj1, obj2, obj3];

var hash = sortOrder.reduce(function(hash, key) {
    hash[key] = objArray.filter(function(item) {
        return (item['groupsIBelongTo'].indexOf(key) > -1);
    });
    return hash;
}, {});

Step 2: Sort the objects and decorate

Using a 'sort order' list, we can then create the objects.

var sortOrder = ['B', 'A', 'C'];
var result = sortOrder.map(function(group) {
    return {
       'groupName': group,
       'objs': hash[group]
    };
});

edit: Changed to use filter to reduce amount of code required.

Note on Efficiency

If you want efficiency, you'll have to be more specific as JavaScript performance differs between engines. Using a for loop or while loop will be faster than using map, reduce and sort. However, it's only a concession of 15% - at least according to my browser. As this other post suggests, it's always best to code for clarity over cleverness.

Community
  • 1
  • 1
Pete
  • 3,246
  • 4
  • 24
  • 43
  • I agree, the array of objects is probably the best solutions for what you're wanting. It also plays well with AngularJS whereas simple objects are more difficult to deal with. – user1789573 Mar 06 '15 at 15:51
  • Excellent answer, thanks a lot! I'll checkout the fiddle and Mozilla pages but this definitely looks way cleaner than what I had written :P. Cheers! – SleepyProgrammer Mar 06 '15 at 16:17