0

When working with data from database, we often get arrays of stuff that, due to database constraints, can be (uniquely) indexed by compound indices. However, indexBy does not seem to work for compound indices, or does it?

Given an array x with objects that have properties a and b, I want to have a dictionary of dictionaries that contain all objects of x, indexed by a and b, respectively. For example:

Fiddle here.

var x = [
    {
        a: 1,
        b: 11,
        c: 101
    },
    {
        a: 2,
        b: 11,
        c: 101
    },
    {
        a: 1,
        b: 11,
        c: 102
    },
    {
        a: 1,
        b: 14,
        c: 102
    },
];

// index x by a, then by b, then by c    
var byABC = _.compoundIndexBy(x, ['a', 'b', 'c']);

// there are two items in `x` with a = 1 and b = 11
console.assert(_.size(byABC[1][11]) == 2, 'Something went wrong...');

// display result
console.log(byABC);

byABC now looks like this:

{
    1: {
        11: {
            101: {
                a: 1,
                b: 11,
                c: 101
            },
            102: {
                a: 1,
                b: 11,
                c: 102
            }
        },
        14: {
            102: {
                a: 1,
                b: 14,
                c: 102
            }
        },
    }
    2: {
        11:{
            101: {
                a: 2,
                b: 11,
                c: 101
            }
        }
    }
}

This Fiddle demonstrates the compoundexIndexBy function. Is my work in vain (because Lo-Dash actually does support compound indices), or can it at least be improved?

Domi
  • 22,151
  • 15
  • 92
  • 122
  • Can you clarify by "printing" the desired result object (`byABC`)? – Guilherme Rodrigues Jan 14 '15 at 10:45
  • @gadr90 Done. I wrote more code to basically simulate a database composite index on my client-side cached data, which is necessary to make this actually useful and manageable. I.e. I also added methods to add and remove data from this "index data structure". – Domi Jan 14 '15 at 11:52

1 Answers1

1

You can create a mixin that recursively groups/indexes your objects:

_.mixin({
    compoundIndexBy: function(lst, iteratees, context) { 
        if (iteratees.length === 1) 
            return _.indexBy(lst, iteratees[0], context);

        var grouped = _.groupBy(lst, iteratees[0], context);

        _.each(grouped, function(sublst, k) {
            grouped[k] = _.compoundIndexBy(sublst, _.rest(iteratees), context);
        });

        return grouped;
    }
});

console.dir(_.compoundIndexBy(x, ['a', 'b', 'c']));

If you prefer a list of objects matching the given indexes (in case of non unique paths, for example):

_.mixin({
    compoundGroupBy: function(lst, iteratees, context) {
        var grouped = _.groupBy(lst, iteratees[0], context);

        if (iteratees.length === 1) 
            return grouped;

        _.each(grouped, function(sublst, k) {
            grouped[k] = _.compoundGroupBy(sublst, _.rest(iteratees), context);
        });

        return grouped;
    }
});
console.dir(_.compoundGroupBy(x, ['a', 'b', 'c']));

And a demo http://jsfiddle.net/nikoshr/8w4n31vb/

nikoshr
  • 32,926
  • 33
  • 91
  • 105