order and organize array of objects - javascript

I have this array of objects and I want to organize the objects based on their units like this:
const array = [
{ unit: 5, id: 'five'},
{ unit: 200, id: 'some22'},
{ unit: 100, id: 'recall'},
{ unit: 5, id: 'some'},
];
// Result :
[
[ { unit: 5, id: 'five'}, { unit: 5, id: 'some'}, ],
[ { unit: 6, id: 'some22'} ],
[ { unit: 55, id: 'recall'} ],
]
Note: we can simply use filter method to get the array but in my case we don't know the units and we just want to order and organize them

You could use a Map:
const array = [
{ unit: 5, id: 'five'},
{ unit: 200, id: 'some22'},
{ unit: 100, id: 'recall'},
{ unit: 5, id: 'some'},
];
const unitGroups = new Map();
for (const obj of array) {
if (!unitGroups.has(obj.unit)) {
unitGroups.set(obj.unit, []);
}
unitGroups.get(obj.unit).push(obj);
}
const result = Array.from(unitGroups.values());
console.log(result);

A different approach using the build in array method reduce, just to create a one-liner. Details to the under appreciated reduce function Array.prototype.reduce
// Initial Data
const array = [
{ unit: 5, id: 'five'},
{ unit: 200, id: 'some22'},
{ unit: 100, id: 'recall'},
{ unit: 5, id: 'some'},
];
let result = array.reduce (function(last, next){
// find index of a List with matching unit
let index = last.findIndex((itemList) => itemList.some( item => item.unit == next.unit));
if(index == -1){ // if no match was found
index = last.push([]) - 1; // add an empty Array and set the index
}
last[index].push(next); // add the Entry to the selected List
return last;
}, [])
console.info(result);
btw.: I'm asuming the posted result data is incorrect, since the "units" for some22 and recall don't match with the initial data. If this assumption is wrong please clarify
Extra: just for kicks and giggles the whole thing as a real one-liner:
( Don't try this at home ;-) )
const array = [
{ unit: 5, id: 'five'},
{ unit: 200, id: 'some22'},
{ unit: 100, id: 'recall'},
{ unit: 5, id: 'some'},
];
console.info( array.reduce((p, c) => ((p.find( l => l.some(i => i.unit == c.unit)) || p[p.push([]) - 1]).push(c), p), []));

Related

How can I access this property in my dictionary in js?

I thought I understood how to loop through a dictionary, but my loop is wrong. I try to access the name of each sub item but my code does not work.
Here is what I did:
list = [
{
title: 'Groceries',
items: [
{
id: 4,
title: 'Food',
cost: 540 ,
},
{
id: 5,
title: 'Hygiene',
cost: 235,
},
{
id: 6,
title: 'Other',
cost: 20,
},
],
}];
function calculateCost(){
let total = 0;
Object.keys(list).forEach((k) => { for (i in k.items) { total += i.data; } });
console.log(total);
return total;
}
Your list is an array includes 1 object and this object has two properties title and items the items here is an array of objects each one of these objects has property cost so to calculate the total cost you need to loop through items array, here is how you do it:
let list = [
{
title: 'Groceries',
items: [
{
id: 4,
title: 'Food',
cost: 540 ,
},
{
id: 5,
title: 'Hygiene',
cost: 235,
},
{
id: 6,
title: 'Other',
cost: 20,
},
],
}];
function calculateCost(){
let total = 0;
list[0].items.forEach(el => {
total += el.cost;
})
console.log(total)
return total;
}
calculateCost();
Your list is an Array, not an Object.
Instead of Object.keys() use Array.prototype.reduce:
const calculateCost = (arr) => arr.reduce((tot, ob) =>
ob.items.reduce((sum, item) => sum + item.cost, tot), 0);
const list = [
{
title: 'Groceries',
items: [
{id: 4, title: 'Food', cost: 10},
{id: 5, title: 'Hygiene', cost: 20},
{id: 6, title: 'Other', cost: 30}
]
}, {
title: 'Other',
items: [
{id: 8, title: 'Scuba gear', cost: 39}
],
}
];
console.log(calculateCost(list)); // 99
Expanding on #Roko's and #mmh4all's answers, the following code adds several verification statements to handle cases where a deeply nested property in your data is not what you expect it to be.
const calculateCost = (orders) => {
let listOfCosts = [];
// For each 'order' object in the 'orders' array,
// add the value of the 'cost' property of each item
// in the order to 'listOfCosts' array.
orders.forEach(order => {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
if (!Array.isArray(order.items)) { return; }
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat
const orderCostArr = order.items.map(item =>
isNaN(item.cost) ? 0 : parseFloat(item.cost, 10));
if (orderCostArr.length === 0) { return; }
// Concatenate 'orderCostArr' to the 'listOfCosts' array
//listOfCosts = listOfCosts.concat(orderCostArry);
// Alternate approach is to use the spread syntax (...) to
// push the items in the array returned by 'order.items.map()'
// into the 'listOfCosts' array.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
listOfCosts.push(...orderCostArr);
});
// Use the 'reduce' method on the 'listOfCosts' array
// to get the total cost.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
const totalCost = listOfCosts.reduce(
(accumulator, currentValue) => accumulator + currentValue, 0);
return totalCost;
};
const list = [
{
title: 'Groceries',
items: [
{ id: 4, title: 'Food', cost: 10 },
{ id: 3, title: 'Baked goods', cost: 20 },
{ id: 5, title: 'Hygiene', cost: 0 },
{ id: 6, title: 'Other' }
]
}, {
title: 'Gear',
items: {},
}, {
title: 'Accessories',
items: [],
}, {
title: 'Bags',
}, {
title: 'Other',
items: [
{ id: 10, title: 'Scuba gear', cost: "5" },
{ id: 8, title: 'Scuba gear', cost: "err" },
{ id: 9, title: 'Scuba gear', cost: 59 }
],
}
];
console.log(calculateCost(list)); // 94

nested for loops to get another object-array output

I need to multiply all the "values" inside "obj1" with the "percent' inside obj2 based on the id of each object. What would be the best way to do that? I've tried with for loop and reduce but I wasn't successful. Any help will be appreciated.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can do that by going through and matching the ids. there are some optimizations that can be made if they are sorted however.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
const x = Object.keys(obj2).map((key,index)=>{
const { id, value } = obj1.find(({id})=>id===obj2[key].id)
return ({id,value:value*obj2[key].percent})
})
console.log(x)
//outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can first create a lookup map using Map, then loop over the obj1 using map to get the desired result
const obj1 = [
{ id: 1, value: 10 },
{ id: 2, value: 10 },
];
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
},
};
const map = new Map();
Object.values(obj2).forEach((v) => map.set(v.id, v));
const result = obj1.map((o) => ({ ...o, value: o.value * map.get(o.id).percent }));
console.log(result);
This should work for you but doesnt handle exeptions if the id doesnt exist in both objects.
// First get the values in an array for easier manipulation
const aux = Object.values(obj2)
const output = obj1.map(ob => {
// Find the id in the other array.
const obj2Ob = aux.find(o => o.id === ob.id) // The id must exist in this aproach
return {
id: ob.id,
value: ob.value * obj2Ob.percent
}
})
console.log(output) // [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]

Flattern object in nested array of arrays Javascript

I have an array of arrays, which contain objects, would like to get the value of a certain key and return it as a big array, have tried a nested map but it returns multiple array's rather than a single array.
const items = [
{
id: 1,
sub_items: [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
},
{
id: 2,
sub_items: [
{
id: 4
},
{
id: 5
},
{
id: 6
}
]
}
]
const subItemIDs = items.map( (item) =>
item.sub_items.map( (subItem) => subItem.id )
)
console.log(subItemIDs);
Expected output
[1, 2, 3, 4, 5, 6]
Actual output
[ [1,2,3], [4,5,6] ]
You can use arrays.flat(). I can provide more specific code once output is mentioned in the question
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]
You could take Array#flatMap to get a flat array from nested arrays.
const
items = [{ id: 1, sub_items: [{ id: 1 }, { id: 2 }, { id: 3 }] }, { id: 2, sub_items: [{ id: 4 }, { id: 5 }, { id: 6 }] }],
subItemIDs = items.flatMap(({ sub_items }) => sub_items.map(({ id }) => id));
console.log(subItemIDs);
Achieved this with:
const items = [
{
id: 1,
sub_items: [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
},
{
id: 2,
sub_items: [
{
id: 4
},
{
id: 5
},
{
id: 6
}
]
}
]
const subItemIDs = [].concat(...items.map( (item) =>
item.sub_items.map( (subItem) => subItem.id )
))
console.log(subItemIDs);
Sometimes, the obvious is the easiest:
Given a data structure that looks like this
const items = [
{ id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
{ id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];
A trivial function like this
function extract_item_ids( items ) {
const ids = [];
for ( const item of items ) {
for ( const {id} of sub_items ) {
ids.push(id);
}
}
return ids;
}
should do the trick. If you want to collect the ids from a tree of any depth, it's just as easy:
function extract_item_ids( items ) {
const ids = [];
const pending = items;
while ( pending.length > 0 ) {
const item = pending.pop();
ids.push(item.id);
pending.push(...( item.sub_items || [] ) );
}
return ids;
}
And collecting the set of discrete item IDs is no more difficult:
If you want to collect the ids from a tree of any depth, it's just as easy:
function extract_item_ids( items ) {
const ids = new Set();
const pending = [...items];
while ( pending.length > 0 ) {
const item = pending.pop();
ids.add(item.id);
pending.push(...( item.sub_items || [] ) );
}
return Array.from(ids);
}
As is the case with most things JavaScript, you have several options. Some are more efficient than others, others have a certain stylistic purity, others might better speak to your fancy. Here are a few:
Array.flat
With array flat you can take your original code and have the JS Engine flatten the array down to a one-dimensional array. Simply append .flat() onto the end of your map.
const items = [
{ id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
{ id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];
const subItemIds = items.map( (item) =>
item.sub_items.map( (subItem) => subItem.id )
).flat()
console.log(subItemIds);
Array.reduce
Another method is to use reduce to iterate over the object and build an accumulation array using Array.reduce. In the example below, when pushing onto the array, the spread operator (...) is used to break the array into elements.
const items = [
{ id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
{ id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];
const subItemIds = items.reduce((arr,item) => (
arr.push(...item.sub_items.map((subItem) => subItem.id)), arr
),[])
console.log(subItemIds);
Other
Other answers here make use of custom functions or Array.flatMap, which should be explored as they could lead to more readable and efficient code, depending on the program's needs.

How can I find and sort the indexes of the elements of an object in array in javascript

Updated Version of the Problem: my goal is to get the indexes of the of the element of this array, with the condition that the subelement param_name of key params will define the index of the object. For instance, object with key name 'caller1' should have a default index of 0, but since param_name is 'caller2' it will take index 1; similarly object 3 with key name 'caller3' will take index 0 since param_name is 'caller1'. For object 2 with key name 'caller2' since param_name is same as key name caller2 it will retain its default index of 1.
const array1 = [{
name: 'caller1',
cost: 12,
params:[{param_name:'caller2',apparatus:'fittings'}]
},
{
name: 'caller2',
cost: 2,
params:[{param_name:'caller2',apparatus:'fittings'}]
},
{
name: 'caller3',
cost: 12,
params:[{param_name:'caller1',apparatus:'valves'}]
}
];
const results = []
for (let j=0; j<array1.length;j++){
results[j] = array1[j].findIndex(a => a.name==array1[j].name);
}
console.log(results);
You need to take the property from params and use that as a search parameter to use when looping over the main array
var indexes = array1.map(element => {
var nameToCheck = element.params[0].param_name;
for (let i = 0; i < array1.length; i++) {
if (array1[i].name == nameToCheck) {
return i;
}
}
})
Demo
const array1 = [{
name: 'caller1',
cost: 12,
params: [{
param_name: 'caller2',
apparatus: 'fittings'
}]
},
{
name: 'caller2',
cost: 2,
params: [{
param_name: 'caller2',
apparatus: 'fittings'
}]
},
{
name: 'caller3',
cost: 12,
params: [{
param_name: 'caller1',
apparatus: 'valves'
}]
}
];
var indexes = array1.map(element => {
var nameToCheck = element.params[0].param_name;
for (let i = 0; i < array1.length; i++) {
if (array1[i].name == nameToCheck) {
return i;
}
}
})
console.log(indexes);
Note that if params actually contains more than 1 element you would need to account for that and decide which one you need to use and change the element.params[0].param_name; line accordingly
You could take a Map and map the indices.
const
array = [{ name: 'caller1', cost: 12, params: [{ param_name: 'caller2', apparatus: 'fittings' }] }, { name: 'caller2', cost: 2, params: [{ param_name: 'caller2', apparatus: 'fittings' }] }, { name: 'caller3', cost: 12, params: [{ param_name: 'caller1', apparatus: 'valves' }] }],
map = new Map(array.map(({ name }, i) => [name, i])),
result = array.map(({ params: [{ param_name }] }) => map.get(param_name));
console.log(result);

Javascript map over array of obj with another array to get different key value

So I am not sure why I having such a difficult time with this, but I have an array of ids that I am trying to use to map over an array of objects to find the corresponding id but return the value from a different key.
i.e.:
arr=[13, 1, 16]
arrObj= [{
id: 1,
name: "cat"
}, {
id: 10,
name: "tiger",
}, {
id: 3,
name: "dog",
}, {
id: 16,
name: "bear",
}, {
id: 8,
name: "fish",
}, {
id: 13,
name: "goat",
}]
and I want it to return:
["goat", "cat", "bear"]
I have a nested map function that does this but returns undefined for the objects that do not have a corresponding ID. I could filter out the undefineds from the returned array, but it seems that there is a cleaner/more efficient way to do this.
What is the cleanest way to achieve this?
You could use Array#map and search with Array#find for the corresponding object. Then take name as return value.
var arr = [13, 1, 16],
arrObj = [{ id: 1, name: "cat" }, { id: 10, name: "tiger" }, { id: 3, name: "dog" }, { id: 16, name: "bear" }, { id: 8, name: "fish" }, { id: 13, name: "goat" }],
result = arr.map(id => arrObj.find(o => o.id === id).name);
console.log(result);
For a lots of data, you could take a Map and build it by mapping key value pairs and then map the result of the map.
var arr = [13, 1, 16],
arrObj = [{ id: 1, name: "cat" }, { id: 10, name: "tiger" }, { id: 3, name: "dog" }, { id: 16, name: "bear" }, { id: 8, name: "fish" }, { id: 13, name: "goat" }],
result = arr.map(
Map.prototype.get,
new Map(arrObj.map(({ id, name }) => [id, name]))
);
console.log(result);
Try this:
var arr=[13, 1, 16];
var arrObj= [{
id: 1,
name: "cat"
}, {
id: 10,
name: "tiger",
}, {
id: 3,
name: "dog",
}, {
id: 16,
name: "bear",
}, {
id: 8,
name: "fish",
}, {
id: 13,
name: "goat",
}];
var result = arr.map(id => arrObj.find(x => x.id == id)).map(x => x.name)
console.log(result);
// ["goat", "cat", "bear"]
.map() (from MDN web docs):
method creates a new array with the results of calling a provided
function on every element in the calling array.
.find() (from MDN web docs):
method returns the value of the first element in the array that
satisfies the provided testing function. Otherwise undefined is
returned.
There have been a lot of good answers already, however i feel the need to push for newer syntax as it is greater to work with in the long run.
const getNamesBasedOnIds = (arr, listOfIds) => {
return arr.reduce(((sum, element) =>
[...sum, ...(listOfIds.includes(element.id) ? [element.name] : [])]),
[]);
}
const animals = getNamesBasedOnIds(arrObj, [13,1,16]);
Using Array.filter, then Array.map requires you to run through the Array max twice.
With Array.reduce you can add elements to the sum if they exists in listOfIds, then after running through the arrObj once you have the result
[...sum, ...(listOfIds.includes(element.id) ? [element.name] : [])] is pretty slow, but its more for showing the use of spear operators
The above is equivalent to
sum.concat(listOfIds.includes(element.id) ? element.name : [])]
The fastest way, is to use Array.push
if(listOfIds.includes(element.id)){
sum.push(element.name);
}
return sum;
The correct way is to not nest any loops:
reduce your array of objects to a map of 'id-values' idValueMap
map through your array of 'ids' idArr using the idValueMap to get
the corresponding values for each id in constant time.
Thought Process
Understand that the biggest problem is the data itself. Don't let insufficient data types or structures force you you have a bad solution/code. Transform the data such to what you need so that you can create a proper solution.
Basically imagine if the objArr was just a Map that you could use to look up the values for an id.... then the solution is straight forward. So let's make that happen and then the rest falls into place. Data Data Data is what I always say :)
Rule of thumb NEVER run a loop inside of a loop if you can help it. FYI: filter, find, map, indexOf .... are all loops internally, do not nest them unless you absolutely must.
This solution is by far the most performant. This way you are not running O(n^2) (very BAD), you are instead running O(n) (very GOOD):
const idArr = [ 13, 1, 16 ];
const objArr= [
{
id: 1,
name: "cat"
},
{
id: 10,
name: "tiger",
},
{
id: 3,
name: "dog",
},
{
id: 16,
name: "bear",
},
{
id: 8,
name: "fish",
},
{
id: 13,
name: "goat",
}
];
const idValueMap = objArr.reduce((acc, { id, name }) => ({ ...acc, [id]: name }), {});
let output = idArr.map((id) => idValueMap[id]);
console.log(output);
You can first use .filter to filter and then use .map to output the desired properties
here
var arr = [13, 1, 16],
arrObj = [{
id: 1,
name: "cat"
}, {
id: 10,
name: "tiger",
}, {
id: 3,
name: "dog",
}, {
id: 16,
name: "bear",
}, {
id: 8,
name: "fish",
}, {
id: 13,
name: "goat",
}];
var res = arrObj.filter(o => arr.indexOf(o.id) >= 0);
console.log(res.map(o => o['name']))

Categories

Resources