Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript object reorganization with performance

I am working on a problem where I have to group an array of objects from one form into another.

An example is better than 1000 words:

var initialData = [
  {
    house: { id: 1, text: "white" },
    room: { id: 1, text: "red" },
    price: 2.1
  },
  {
    house: { id: 1, text: "white" },
    room: { id: 2, text: "blue" },
    price: 3.1
  },
  {
    house: { id: 1, text: "white" },
    room: { id: 3, text: "red" },
    price: 5.8
  },
  {
    house: { id: 2, text: "black" },
    room: { id: 1, text: "yellow" },
    price: 9.1
  },
  {
    house: { id: 2, text: "black" },
    room: { id: 2, text: "green" },
    price: 7.7
  },
];

And the new object should look like this:

var finalObject = {
  houses: [
    {
      id: 1, text: "white",
      rooms: [
        { id: 1, text: "red", price: "2.1" },
        { id: 2, text: "blue", price: "3.1" },
        { id: 3, text: "red", price: "5.8" }
      ]
    },
    {
      id: 2, text: "black",
      rooms: [
        { id: 1, text: "yellow", price: "9.1" },
        { id: 2, text: "green", price: "7.7" }
      ]
    }
  ]
};

I have to find unique houses with all their rooms and also to add each price from initial object inside room.

I'm wondering which would be the best way to do this since I will have a huge amount of elements?

I have some ideas with multiple loops, but to me it seems a bit too complex my solution.

Update: my question is not the same as the one candidate for duplication because I don't use lodash, and my object has to be refactored a bit, not just regrouped.

Possible solution (inspired by @Gael's answer)

finalObject = {}

for (var i = 0; i < initialData.length; ++i) {
  var item = initialData[i];
  var id = item.house.id;
  if(!finalObject[id]) {
    finalObject[id] = item.house;
    finalObject[id].rooms = [];
  }
  var room = item.room;
  room.price = item.price;

  finalObject[id].rooms.push(room);
}

console.log(finalObject);
like image 390
V. Sambor Avatar asked Jun 23 '17 20:06

V. Sambor


3 Answers

Use Array#reduce with a helper object:

var initialData = [{"house":{"id":1,"text":"white"},"room":{"id":1,"text":"red"},"price":2.1},{"house":{"id":1,"text":"white"},"room":{"id":2,"text":"blue"},"price":3.1},{"house":{"id":1,"text":"white"},"room":{"id":3,"text":"red"},"price":5.8},{"house":{"id":2,"text":"black"},"room":{"id":1,"text":"yellow"},"price":9.1},{"house":{"id":2,"text":"black"},"room":{"id":2,"text":"green"},"price":7.7}];

var dict = {}; // helper object
var result = initialData.reduce(function(houses, obj) { // reduce the data
  var house = dict[obj.house.id]; // get the house from the dict by id
  
  if(!house) { // if house wasn't found
    house = Object.assign({}, obj.house, { rooms: [] }); // create a new house object
    houses.push(house); // push it into the array of houses
    dict[house.id] = house; // add it to the dict by id
  }
  
  house.rooms.push(obj.room); // push the room to the current house
  
  return houses;
}, []);

console.log(result);

You can also achieve it using ES6 Map and spread syntax:

const initialData = [{"house":{"id":1,"text":"white"},"room":{"id":1,"text":"red"},"price":2.1},{"house":{"id":1,"text":"white"},"room":{"id":2,"text":"blue"},"price":3.1},{"house":{"id":1,"text":"white"},"room":{"id":3,"text":"red"},"price":5.8},{"house":{"id":2,"text":"black"},"room":{"id":1,"text":"yellow"},"price":9.1},{"house":{"id":2,"text":"black"},"room":{"id":2,"text":"green"},"price":7.7}];

const result = [...initialData.reduce((houses, { house, room }) => { // reduce the data to a Map
  const currentHouse = houses.get(house.id) || Object.assign({}, house, { rooms: [] }); // get the current house from the map by id, or create a new one
  
  currentHouse.rooms.push(room); // push the room to the current house
  
  return houses.set(currentHouse.id, currentHouse); // set the house to the map, and return it
}, new Map()).values()]; // get the values of the map and spread to an array

console.log(result);
like image 176
Ori Drori Avatar answered Sep 19 '22 01:09

Ori Drori


You should use a map:

var myMap = new Map();

var keyObj = {}, // ideally your room object with id


// setting the values
myMap.set(keyString, "value associated with 'a string'"); // not recommended for your case

Or:

myMap.set(keyObj, 'value associated with keyObj'); // should be rooms

myMap.size; // the number of items you added
like image 31
Pritam Banerjee Avatar answered Sep 21 '22 01:09

Pritam Banerjee


var houses= {};

initialData.forEach(function(item){
  
  if( !houses[ item.house ] ){
  
    houses[ item.house ]= item.house;
    houses[ item.house ].rooms= {};
  }
  houses[ item.house ].rooms[ item.room.id ]= item.room;
  houses[ item.house ].rooms[ item.room.id ].price= item.price;
  
});
like image 23
Gaël Barbin Avatar answered Sep 20 '22 01:09

Gaël Barbin