0

Lately I find myself using associative arrays (or hastables) more and more in my applications (advantages being lookup speed, duplicate removal etc).

Here is typical code taken from my application -

 var parent1 = { id: 5, name: 'parent 1'}, parent2  = { id: 10, name: 'parent 2'};
 var children = [{id: 1, parent: parent1}, {id: 2, parent: parent1}, {id: 3, parent: parent1}, {id: 4, parent: parent2}]

var addToHash = function (hashObj, child) {
    var parent = child.parent;
    hashObj[parent.id] = parent;
    return hashObj;
};

var uniqueParents = R.reduce(addToHash, {}, children);

At the moment I use the Ramda reduce function.

Question - Is there a better (more succinct) way to do this either with Ramda, another 3rd party lib or simply in plain vanilla Javascript?

Joseph King
  • 5,089
  • 1
  • 30
  • 37

2 Answers2

1

reduce (either Ramda's or JavaScript's own) doesn't buy you anything here, it just adds an opportunity to write a bug (forgetting to return hashObj) and I'd argue subjectively that the semantics are off. reduce is for when the accumulator changes, as in the classic example of summing up the values in an array.

You can make it more succinct by giving yourself a reusable function that's more tailored to the job at hand:

function buildHash(hash, array, callback, thisArg) {
    array.forEach(function(entry) {
        callback.call(thisArg, hash, entry);
    });
    return hash;
}

Then:

var addToHash = function(hash, child) {
    var parent = child.parent;
    hash[parent.id] = parent;
};
var uniqueParents = buildHash({}, children, addToHash);

Or as an Array.prototype extension you add with Object.defineProperty so unsuspecting for-in loops don't get tripped up:

Object.defineProperty(Array.prototype, "buildHash", {
    value: function buildHash(hash, array, callback, thisArg) {
        array.forEach(function(entry) {
            callback.call(thisArg, hash, entry);
        });
        return hash;
    }
});

Then it's the same addToHash, but the call is more succinct:

var uniqueParents = children.buildHash({}, addToHash);

And all of these get more brief if you're using ES2015's arrow functions. :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

One possibility:

R.pipe(
  R.map(prop('parent')),                //=> [parent1, parent1, parent1, parent2]
  R.uniq,                               //=> [parent1, parent2]
  R.map(parent => [parent.id, parent]), //=> [[5, parent1], [10, parent1]]
  R.fromPairs                           //=> {5: parent1, 10: parent2}
)(children); 
//=> {5: {id: 5, name: 'parent 1', 10: {id: 10, name: 'parent 2'}}}

Note that if you just want the list of uniq parents and not the hashmap, you could stop after the second function in the pipeline.

Another option, similar to yours, which simply turns your function into one which returns a new accumulator rather than mutates it is:

var parentMap = R.reduce((acc, child) => R.assoc(child.parent.id, child.parent, acc), {});
parentMap(children); //=> {5: {id: 5, name: 'parent 1', 10: {id: 10, name: 'parent 2'}}}
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103