Amir Popovich
Amir Popovich

Reputation: 29846

Converting an array to a key-value object using a method similar to Array.prototype.map()?

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)

Upvotes: 1

Views: 64

Answers (4)

Robert
Robert

Reputation: 2669

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.

Upvotes: 1

RobG
RobG

Reputation: 147483

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);

Upvotes: 0

c-smile
c-smile

Reputation: 27470

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

Upvotes: 2

Kelsadita
Kelsadita

Reputation: 1038

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);

Upvotes: 0

Related Questions