1

Is there any Array method similar to Array.prototype.map that takes an array and converts it into a key-value object?

Here's an example:

var people = [{name: 'person1', tableId: 1}, {name: 'person2', tableId: 2}];
var table = [{id:1, shape:2, title: 'table1'}, {id:2, shape:4, title: 'table2'}];

I would like to create a table-people object where the key will be an id and the value would be all the people that have the relevant tableId.

An example for the output will be:

var output = {
  1: [{name: 'person1', tableId: 1}, <more people>], 
  2: [{name: 'person2', tableId: 2}}, <more people>]
};

output will be a key-value object. The key is the tableId and the value is an array of people. A person has some more information(other than name and tableId - this is just a simple example).

Now the easy way to do it is:

var output = {};
for(var i = 0 ; i < table.length ; i++)
{
   output[table.id] = getPeopleThatSitInTable(table.id); // will return an array
}

Is there any method like:

table.keyValueMapObject(function(item){
  return { key: item.id, value: getPeopleThatSitInTable(item.id) }
});

This method will create an object behind the scenes, fill it based on key & value and return the object?

I'm not looking for something like:

var obj = {}; // declaring an object
// loop somehow and fill obj(for\foreach\map\reduce etc.)

Since using for does the same thing in "the best way".

I'm looking for something like:

var obj = table.methodName(filterFunction)
4
  • Array.prototype.map is generic, you can supply a plain Object as this. Can you provide an example of the required output? Commented Dec 20, 2014 at 6:25
  • 1
    Your code is too confusing and output-input as well. Can you just create a simple input and how a call to a function should produce the output? Commented Dec 20, 2014 at 6:47
  • I don't see how the table array fits into the scheme. Commented Dec 20, 2014 at 6:48
  • @AmitJoki & RobG - I've edited my post. I'm looking for a key-value mapper. The key will be the tableId and the value will be an array of people who are suppose to sit in that table. I can do the mapping alone with a for\foreach and I was looking for a built in method(similar to Array.prototype.map) that could do that for me. Does anything like this exist? Commented Dec 20, 2014 at 6:56

4 Answers 4

2

Check _.groupBy() source. It does exactly what you want.

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

1 Comment

Underscore isn't an option for me as for now, but it's good to know that something like this exists, Thanks!
1

I think the most relevant Array method in this case would be reduce. Though it doesn't help much, you're reducing the table to a key-value object:

var result = table.reduce(function(result, entry) {
    result[entry.id] = people.filter(function(person) { // pretty inefficient
        return person.tableId === entry.id;
    });
    return result;
}, {}); // initial key-value object

But actually this use of reduce is a bit absurd because the callback doesn't really combine two values. It's just modifying and passing the initial object. While it's making the intent clear it's the same as using forEach:

var result = {}; // initial key-value object
table.forEach(function(entry) {
    result[entry.id] = people.filter(function(person) { // pretty inefficient
        return person.tableId === entry.id;
    });
});

And even then filtering the whole people array is pretty inefficient. You should only iterate over it once.

var result = {}; // initial key-value object
table.forEach(function(entry) {
    result[entry.id] = [];
});
people.forEach(function(person) {
    result[person.tableId].push(person);
});

I don't think there's much potential for simplification.

Of course with the power of Object.assign, the spread operator, array comprehensions, computed property names, object destructuring and arrow functions you could write it as follows:

var result = Object.assign({}, ...[{
    [id]: people.filter(
        ({tableId}) => tableId === id
    )
} for({id} of table)]);

But it's almost identical to the first version. Again it's inefficient. And it only seems to work in Firefox currently.

I suppose there won't be a native equivalent anytime soon.

1 Comment

That's the 'primitive' way(for\foreach are equivalent to me). Since I found myself doing this a lot I've created a method that does this - But I'd like to get something built in for this. Thanks for your answer!
0

You can use forEach to iterate over tables and construct table-people object by filtering people based on tableId as follows,

"use strict";

var people = [{name: 'person1', tableId: 1}, {name: 'person2', tableId: 2}];
var table = [{id:1, shape:2, title: 'table1'}, {id:2, shape:4, title: 'table2'}];

var tablePeopleObj = {};
table.forEach(function (tableItem) {
  tablePeopleObj[tableItem.id] = getPeople(tableItem.id);
});


function getPeople(tableId) {
  return people.filter(function function_name (person) {
    return person.tableId == tableId;
  });
}

console.log(tablePeopleObj);

3 Comments

I know about foreach - The problem is that you wont have old browser support and that it's similar to doing a for. I'm looking for a method similar to map that will return an object(based on a key) and not an array. Thank you anyway!!!
@AmirPopovich—if you are concerned about old browser support, for and for..in are your only options. ECMAScript ed 3 didn't have iterators for Array and Object.
@RobG - You're right - lets ignore the old browsers for a second..I'm looking for a solution that doesn't make me declare a var, loop it(for\foreach etc) and do my stuff. I'm looking for an internal method that will do this for me - something similiar to "the map() way". Thanks!
0

The following uses Array.prototype.map, but likely isn't what you want. Consider it just an example of use that might be helpful:

var people = [{name: 'person1', tableId: 1}, {name: 'person2', tableId: 2}];
var obj = {};

people.map(function(v, i) {
  var key = v.tableId;
  if (!this.hasOwnProperty(key)) {
    this[key] = [];
  }
  this[key].push(v);
}, obj);

2 Comments

Still you're declaring an object, filling it up using a loop(or map) and misusing Array.prototype.map. I think it's a lot better to use a simple for\foreach in this case. Thank you for your answer.
If you want to "convert" an Array to an Object, then you've got to create a new object. I don't think this is misusing map, it's intended to be a generic method for traversing own properties and allows setting of this. I think it's more appropriate than reduce.

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.