Reorganizing a Javascript Object with Performance

I am working on a problem when I need to group an array of objects from one form to 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 need to find unique houses with all their rooms, and also add every price from the original object inside the room.

I am wondering what would be the best way to do this, since I will have a huge amount of elements?

I have some ideas with several cycles, but for me it seems too complicated for my solution.

Update: my question does not coincide with the only candidate for duplication, because I do not use lodash, and my object should be reorganized a bit, and 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); 
+5
source share
4 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 this with ES6 Map and distribution 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); 
+4
source

You must use the card:

 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 
+2
source

Depending on whether you want to keep the original data or not, this will affect the decision. I suggested that you want to keep the original object and create a new one.

You can do this in one loop if you use the Map object to remember where the existing id houses are. If the house is already inserted in the result, you just need to add a new room. Otherwise, you need to create a new home object and save it on the map.

 function transformHouses(houses) { const houseMap = new Map(); const result = {houses: []}; for(const house of houses) { if(houseMap.has(house.id)) { const index = houseMap.get(house.id); const room = Object.assign({price: house.price}, house.room); result.houses[index].rooms.push(room); } else { const room = Object.assign({price: house.price}, house.room); const entry = Object.assign({rooms: [room]}, house.house) housesMap.set(house.id, result.houses.length); result.houses.push(entry); } } return result; } 
+1
source

 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; }); 
+1
source

Source: https://habr.com/ru/post/1269166/


All Articles