GrahamB
GrahamB

Reputation: 569

Underscore creating arrays based on object key

Let's say I have a js object such as this

{
    a: 5,
    b: 1,
    c: 3
}

What I would like to do using Underscore is generate an array which would resemble the following;

["a","a","a","a","a","b","c","c","c"]

I can achieve what I want with the following code

var i = {
    a: 5,
    b: 1,
    c: 3
};

unpack = function (i, item) {
    var items = [];
    _.range(0, i).forEach(function (i) {
        items.push(item)
    })
    return items;
}

console.log(_.flatten(_.map(i, unpack)));

http://jsfiddle.net/VCFZx/ but it doesnt seem the tidiest way of doing it?

Upvotes: 3

Views: 135

Answers (2)

Xotic750
Xotic750

Reputation: 23472

In ECMA5 you could do it like this

Javascript

function createFilledArray(obj) {
    return Object.keys(obj).reduce(function(acc, key) {
        var repeat = obj[key],
            count;

        for(count = 0; count < repeat; count += 1) {
            acc.push(key);
        }

        return acc;
    }, []);
}

var a = {
    'a': 5,
    'b': 1,
    'c': 3
};

console.log(createFilledArray(a));

On jsFiddle

Which in terms of underscore would look like this

Javascript

function createFilledArray(obj) {
    return _.reduce(_.keys(obj), function(acc, key) {
        var repeat = obj[key],
            count;

        for(count = 0; count < repeat; count += 1) {
            acc.push(key);
        }

        return acc;
    }, []);
}

On jsFiddle

Which if you wanted to go further and replace the for

Javascript

function createFilledArray(obj) {
    return _.reduce(_.keys(obj), function(acc, key) {
        _.times(obj[key], function() {
            acc.push(key);
        });

        return acc;
    }, []);
}

On jsFiddle

And now for a little interest factor, lets have a look at the performances of the above 3 functions as we introduce more of Underscores methods, and we'll also compare them against the accepted solution by @mrhobo

Here is the jsPerf

Something else that none of these take into account, and it is unclear from your question, is whether alphabetical order is a must. If it is then a sort will need to be thrown into the mix, as none of these guarantee the ordering.

Finally, in ES3 as mentioned in the comments.

Javascript

function createFilledArray(obj) {
    var acc = [],
        key,
        repeat,
        count;

    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            for (count = 0, repeat = obj[key]; count < repeat; count += 1) {
                acc.push(key);
            }
        }
    }

    return acc;
}

On jsFiddle

I have added the ES3 method to the jsPerf also.

And if you want to chain the result with other Underscore methods.

_.chain(createFilledArray(a)).forEach(function(element) {
    console.log(element);
});

Upvotes: 2

Lodewijk Bogaards
Lodewijk Bogaards

Reputation: 19987

How about this:

var obj = 
{
a :5,
b : 1,
c : 3
};

console.log( 
  _.flatten(_.map(_.keys(obj), function (key) {
      return _.times(obj[key], function() { return key; });  
  }))
);

http://jsfiddle.net/J6bqb/

Note that this solution is purely functional.

Here is a nicely chained version:

  _.chain(_.keys(obj))
    .map(function (key) {
      return _.times(obj[key], function() { return key; });  })
    .flatten()
    .value()

Upvotes: 2

Related Questions