How does one order array-items of complex data-structures by a specific but deeply nested property-value? - javascript

This is my array, I have an object and then a count of how many repeats Id have for example the first object has the Id 2 repeated 3 times.
[{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}]
Now I need to order this array based on an Id for example the number "9" so if the first object has the Maximus count of the id 9 of all it will be the first and the others whit minus count would be bellow, like this, the number 9 will be a random number.
[{
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}]

Using Array.prototype.sort one needs to write a function which compares two array/list items according to the OP's requirements.
Such a comparator is expected to return a number value either grater than Zero or lower than Zero or Zero itself in case of item equality.
Thus one needs to find two different counts, one count for each item which will be found by searching an item's Category array by an additionally provided id value.
In order to keep the compare function reusable it is implemented as a function which allows a context to be bound to it which in the OP's case is an object that features the id one is looking for ... e.g. something like ... { id: 9 } or { id: 4 } ...
function compareByBoundIdCountOfItemCategoryList(a, b) {
const { id } = this;
const aCount = a.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
const bCount = b.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
// in case of equal counts compare the `Category` array's lengths'.
return (bCount - aCount) || (b.Category.length - a.Category.length);
}
const sampleList = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
console.log(
'{ id: 9 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 9 }))
);
console.log(
'{ id: 4 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 4 }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
As one of the comments does point out, the above code requires a version of at least node 14.0.0 due to the function compareByBoundIdCountOfItemCategoryList which uses both the Optional Chaining Operator / ?. and the Nullish Coalescing Operator / ??.
In order to let the script not break one has to replace the line ...
... aCount = a.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
... with this alternative ...
... aCount = (a.Category.find(ctgry => ctgry.Id === id) || { count: -1 }).count;
function compareByBoundIdCountOfItemCategoryList(a, b) {
const { id } = this;
const aCount = (
a.Category.find(ctgry => ctgry.Id === id) ||
{ count: -1 }
).count;
const bCount = (
b.Category.find(ctgry => ctgry.Id === id) ||
{ count: -1 }
).count;
// in case of equal counts compare the `Category` array's lengths'.
return (bCount - aCount) || (b.Category.length - a.Category.length);
}
const sampleList = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
console.log(
'{ id: 9 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 9 }))
);
console.log(
'{ id: 4 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 4 }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

I find this format a simpler alternative to the answer from Peter Seliger. We simply store the sought id in a closure when creating the comparator function we pass to sort:
const byCategoryCount = (categoryId) => ({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) ?.count ?? -1) -
(c1 .find (({Id}) => Id === categoryId) ?.count ?? -1)
const input = [{Id: 1, Info: "Info", Category: [{Id: 2, count: 3}, {Id: 4, count: 1}, {Id: 8, count: 1}, {Id: 18, count: 1}, {Id: 9, count: 1}, {Id: 3, count: 1}]}, {Id: 2, Info: "Info 2", Category: [{Id: 2, count: 3}, {Id: 9, count: 2}, {Id: 21, count: 1}, {Id: 3, count: 1}]}, {Id: 3, Info: "Info 3", Category: [{Id: 4, count: 1}, {Id: 11, count: 1}, {Id: 9, count: 1}]}]
console .log (input .sort (byCategoryCount (9)))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you don't have the nullish coalescing operator available in your environment, this variant is not much worse:
const byCategoryCount = (categoryId) => ({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) || {count: -1}) .count -
(c1 .find (({Id}) => Id === categoryId) || {count: -1}) .count
We could also choose to write a wrapper function that returns a sorted version without mutating the original list. It might look like this:
const sortByCategoryCount = (categoryId, xs) =>
[... xs] .sort (byCategoryCount (categoryId))
But at that point we might start to wonder whether the helper function is offering us anything and we might choose to refactor to
const sortByCategoryCount = (categoryId, xs) =>
[... xs] .sort (({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) || {count: -1}).count -
(c1 .find (({Id}) => Id === categoryId) || {count: -1}).count
)

This should work for you sortByCount:
var my_arr = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
function sortByCount(arr, targetId){
var arr_temp = [];
arr.forEach(el => {
var elem = el.Category.filter(e => e.Id === targetId)[0];
var value = elem ? elem.count : -1;
arr_temp.push({
value: value,
obj: el
});
});
arr_temp.sort((a,b)=> b.value - a.value);
return arr_temp.map(el => el.obj);
}
var sortedArr = sortByCount(my_arr, 9);
console.log(sortedArr)

Related

How to transform a dataset and aggregate values with javascript/typescript

I want to take a dataset which is an object where the the value field has an array value. I want to iterate through the array and group them by the different ids. I also want it to get an aggregate of "count"(total count) and have a field for the different type of "status" and its associated count.
"value": [
{
"count": 5,
"id": "id1",
"status": 1
},
{
"count": 2,
"id": "id1",
"status": 3
},
{
"count": 2,
"id": "id1",
"status": 4
},
{
"count": 1,
"id": "id2",
"status": 0
},
{
"count": 2,
"id": "id2",
"status": 1
},
{
"count": 7,
"id": "id2",
"status": 2
},
{
"count": 5,
"id": "id2",
"status": 3
},
{
"count": 3,
"id": "id2",
"status": 4
}
]
}```
desired output is a map where the keys are ids and value is made up of totalCount(aggregate of all the counts of that id), statusx(where x is the different type of status. There will be a limited type of status. Maybe 5 or so) that has the count of that status, and "status" to show what type of status it is(0-5):
{
id1 : { totalCount: 9, status1 : 5, status3: 2, status4:2},
id2 : { totalCount: 18, status0 : 1, status1 : 2, status2: 7, status3: 5, status4:3}
}
Super specific and weird ask, but here ya go. You can do this easily with the reduce method:
const object = {
value: [
{
count: 5,
id: 'id1',
status: 1,
},
{
count: 2,
id: 'id1',
status: 3,
},
{
count: 2,
id: 'id1',
status: 4,
},
{
count: 1,
id: 'id2',
status: 0,
},
{
count: 2,
id: 'id2',
status: 1,
},
{
count: 7,
id: 'id2',
status: 2,
},
{
count: 5,
id: 'id2',
status: 3,
},
{
count: 3,
id: 'id2',
status: 4,
},
],
};
interface Output {
[key: string]: {
totalCount: number;
[key: string]: number;
};
}
const group = ({ value }: typeof object) => {
return value.reduce((acc: Output, { count, id, status }) => {
if (!acc[id]) acc[id] = { totalCount: count };
else acc[id].totalCount += count;
if (!acc[id][`status${status}`]) acc[id][`status${status}`] = 1
else acc[id][`status${status}`] += 1
return acc;
}, {} as Output);
};
console.dir(group(object));
Compiled:
"use strict";
const object = {
value: [
{
count: 5,
id: 'id1',
status: 1,
},
{
count: 2,
id: 'id1',
status: 3,
},
{
count: 2,
id: 'id1',
status: 4,
},
{
count: 1,
id: 'id2',
status: 0,
},
{
count: 2,
id: 'id2',
status: 1,
},
{
count: 7,
id: 'id2',
status: 2,
},
{
count: 5,
id: 'id2',
status: 3,
},
{
count: 3,
id: 'id2',
status: 4,
},
],
};
const group = ({ value }) => {
return value.reduce((acc, { count, id, status }) => {
if (!acc[id])
acc[id] = { totalCount: count };
else
acc[id].totalCount += count;
if (!acc[id][`status${status}`])
acc[id][`status${status}`] = 1;
else
acc[id][`status${status}`] += 1;
return acc;
}, {});
};
console.dir(group(object));
This is written in TypeScript. You can refer to: https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html if you want to learn more about the 'index access types'
Edit: https://stackblitz.com/edit/typescript-cbfodv, working stackblitz code is available here.
const dataArray = [
{
count: 5,
id: 'id1',
status: 1,
},
{
count: 2,
id: 'id1',
status: 3,
},
{
count: 2,
id: 'id1',
status: 4,
},
{
count: 1,
id: 'id2',
status: 0,
},
{
count: 2,
id: 'id2',
status: 1,
},
{
count: 7,
id: 'id2',
status: 2,
},
{
count: 5,
id: 'id2',
status: 3,
},
{
count: 3,
id: 'id2',
status: 4,
},
];
var obj: { [k: string]: any } = {};
dataArray.forEach((elem) => {
let foundGroup = obj[elem.id];
let statusPropertyName = 'status' + elem.status;
if (foundGroup === undefined) {
obj[elem.id] = {
totalCount: elem.count,
[statusPropertyName]: elem.count,
};
} else {
foundGroup['totalCount'] += elem.count;
let foundStatus = foundGroup[statusPropertyName];
if (foundStatus === undefined) {
foundGroup[statusPropertyName] = elem.count;
} else {
foundStatus += elem.count;
}
}
});
console.log(obj);

Reduce and sum array of objects (JS) [duplicate]

This question already has answers here:
Sum JavaScript object propertyA values with the same object propertyB in an array of objects
(12 answers)
Group by, and sum, and generate an object for each array in JavaScript
(4 answers)
How to group by and sum an array of objects? [duplicate]
(2 answers)
Group objects by multiple properties in array then sum up their values
(16 answers)
Closed 3 years ago.
My data:
arr: [],
models: [
{ id: 1, name: "samsung", seller_id: 1, count: 56 },
{ id: 1, name: "samsung", seller_id: 2, count: 68 },
{ id: 2, name: "nokia", seller_id: 2, count: 45 },
{ id: 2, name: "nokia", seller_id: 3, count: 49 }
]
Expected Arr:
arr: [
{ id: 1, name: "samsung", count: 124 },
{ id: 2, name: "nokia", count: 94 }
]
It's my code to simplify models by removing repeated id:
this.models.forEach(mdl => {
if (!this.arr.some(obj => obj.id === mdl.id)) {
this.arr.push(mdl);
}
});
But I can't sum counts.
How can I do that?
You can use Array.reduce():
var models = [
{ id: 1, name: "samsung", seller_id: 1, count: 56 },
{ id: 1, name: "samsung", seller_id: 2, count: 68 },
{ id: 2, name: "nokia", seller_id: 2, count: 45 },
{ id: 2, name: "nokia", seller_id: 3, count: 49 }
];
var arr = models.reduce((acc, item) => {
let existItem = acc.find(({id}) => item.id === id);
if(existItem) {
existItem.count += item.count;
} else {
acc.push(item);
}
return acc;
}, []);
console.log(arr);
So, for your code, you can use this.arr and this.models replacing those variables from above which will look something like:
this.arr = this.models.reduce((acc, item) => {
let existItem = acc.find(({id}) => item.id === id);
if(existItem) {
existItem.count += item.count;
} else {
acc.push(item);
}
return acc;
}, []);
You can use Object.values instead of some. Inside the reduce callback create an object with key as id and value from the models. Then use Object.values to create an array of the values
let models = [{
id: 1,
name: "samsung",
seller_id: 1,
count: 56
},
{
id: 1,
name: "samsung",
seller_id: 2,
count: 68
},
{
id: 2,
name: "nokia",
seller_id: 2,
count: 45
},
{
id: 2,
name: "nokia",
seller_id: 3,
count: 49
}
]
let data = models.reduce((acc, curr) => {
if (!acc[curr.id]) {
acc[curr.id] = curr;
} else {
acc[curr.id].count += curr.count
}
return acc;
}, {})
console.log(Object.values(data))

How to return the sum of values from an array of objects based inside an array of objects

I have the following example array of objects, each object in the array contains an id, personId, scores and finally score. In some objects the scores is either null or it contains another array of objects which are the scores. In other objects the score may contain a value instead of being null. Finally, there may be a case when the object can contain both scores and score.
const startingArray = [
{
id: 1,
personId: 1,
scores: [
{
id: 1,
title: 'Google',
score: 12
},
{
id: 2,
title: 'Bing',
score: 23
},
{
id: 3,
title: 'Facebook',
score: 34
}
],
score: null
},
{
id: 2,
personId: 1,
scores: null,
score: 123
},
{
id: 3,
personId: 2,
scores: [
{
id: 4,
title: 'Google',
score: 7
},
{
id: 5,
title: 'Bing',
score: 32
},
{
id: 6,
title: 'Facebook',
score: 9
}
],
score: null
},
{
id: 4,
personId: 3,
scores: null,
score: 106
},
{
id: 5,
personId: 3,
scores: [
{
id: 7,
title: 'Google',
score: 6
},
{
id: 8,
title: 'Bing',
score: 4
},
{
id: 9,
title: 'Facebook',
score: 3
}
],
score: 5
}
]
I can filter the startingArray to return the valid objects for a person:
startingArray.filter(item => item.personId === personId)
And I also figured out how to use map and reduce to return a value of the score items for the person:
startingArray.filter(item => item.personId === personId).map(item => item.score).reduce((a, b) => a + b, 0)
Where I'm struggling is to be able to sum the score items in the scores array where it's set against a person.
Ultimately what I'm after is to be able to call personScores(1) and it return the total of the scores for person 1 (i.e. 69), or call personScores(3) and it would return 124 (i.e. 106 + 13 + 5).
It's not clear whether a person can appear more than once in the startingArray. Assuming they can appear more than once:
One popular way to do it would be to use Array#reduce, but I'd just use a couple of for-of loops. You don't need to pre-filter (although some prefer to, which is fine).
Here's the for-of version:
function personScore(personId) {
let sum = 0;
for (const entry of startingArray) {
if (entry.personId === personId) {
sum += entry.score || 0;
if (entry.scores) {
for (const {score} of entry.scores) {
sum += score;
}
}
}
}
return sum;
}
Live Copy:
const startingArray = [
{
id: 1,
personId: 1,
scores: [
{
id: 1,
title: 'Google',
score: 12
},
{
id: 2,
title: 'Bing',
score: 23
},
{
id: 3,
title: 'Facebook',
score: 34
}
],
score: null
},
{
id: 2,
personId: 1,
scores: null,
score: 123
},
{
id: 3,
personId: 2,
scores: [
{
id: 4,
title: 'Google',
score: 7
},
{
id: 5,
title: 'Bing',
score: 32
},
{
id: 6,
title: 'Facebook',
score: 9
}
],
score: null
},
{
id: 4,
personId: 3,
scores: null,
score: 106
},
{
id: 5,
personId: 3,
scores: [
{
id: 7,
title: 'Google',
score: 6
},
{
id: 8,
title: 'Bing',
score: 4
},
{
id: 9,
title: 'Facebook',
score: 3
}
],
score: 5
}
]
function personScore(personId) {
let sum = 0;
for (const entry of startingArray) {
if (entry.personId === personId) {
sum += entry.score || 0;
if (entry.scores) {
for (const {score} of entry.scores) {
sum += score;
}
}
}
}
return sum;
}
console.log(personScore(1));
Here's the reduce version:
function personScore(personId) {
return startingArray.reduce((sum, entry) => {
if (entry.personId !== personId) {
return sum;
}
sum += entry.score || 0;
if (entry.scores) {
sum += entry.scores.reduce((acc, {score}) => acc + score, 0);
}
return sum;
}, 0);
}
Live Copy:
const startingArray = [
{
id: 1,
personId: 1,
scores: [
{
id: 1,
title: 'Google',
score: 12
},
{
id: 2,
title: 'Bing',
score: 23
},
{
id: 3,
title: 'Facebook',
score: 34
}
],
score: null
},
{
id: 2,
personId: 1,
scores: null,
score: 123
},
{
id: 3,
personId: 2,
scores: [
{
id: 4,
title: 'Google',
score: 7
},
{
id: 5,
title: 'Bing',
score: 32
},
{
id: 6,
title: 'Facebook',
score: 9
}
],
score: null
},
{
id: 4,
personId: 3,
scores: null,
score: 106
},
{
id: 5,
personId: 3,
scores: [
{
id: 7,
title: 'Google',
score: 6
},
{
id: 8,
title: 'Bing',
score: 4
},
{
id: 9,
title: 'Facebook',
score: 3
}
],
score: 5
}
]
function personScore(personId) {
return startingArray.reduce((sum, entry) => {
if (entry.personId !== personId) {
return sum;
}
sum += entry.score || 0;
if (entry.scores) {
sum += entry.scores.reduce((acc, {score}) => acc + score, 0);
}
return sum;
}, 0);
}
console.log(personScore(1));
If they can appear only once, an array really isn't the way to organize that data (I'd use an object or a Map), but with an array I'd use find to find them, and then just get the information from that one entry:
function personScore(personId) {
const entry = startingArray.find(entry => entry.personId === personId);
if (!entry) {
return 0;
}
let sum = entry.score || 0;
if (entry.scores) {
for (const {score} of scores) {
sum += score;
}
}
return sum;
}
Live Copy:
const startingArray = [
{
id: 1,
personId: 1,
scores: [
{
id: 1,
title: 'Google',
score: 12
},
{
id: 2,
title: 'Bing',
score: 23
},
{
id: 3,
title: 'Facebook',
score: 34
}
],
score: null
},
{
id: 2,
personId: 1,
scores: null,
score: 123
},
{
id: 3,
personId: 2,
scores: [
{
id: 4,
title: 'Google',
score: 7
},
{
id: 5,
title: 'Bing',
score: 32
},
{
id: 6,
title: 'Facebook',
score: 9
}
],
score: null
},
{
id: 4,
personId: 3,
scores: null,
score: 106
},
{
id: 5,
personId: 3,
scores: [
{
id: 7,
title: 'Google',
score: 6
},
{
id: 8,
title: 'Bing',
score: 4
},
{
id: 9,
title: 'Facebook',
score: 3
}
],
score: 5
}
]
function personScore(personId) {
const entry = startingArray.find(entry => entry.personId === personId);
if (!entry) {
return 0;
}
let sum = entry.score || 0;
if (entry.scores) {
for (const {score} of scores) {
sum += score;
}
}
return sum;
}
console.log(personScore(1));
You can use reduce to get the sum and either use reduce again if we have an array for scores, or simply add what is at score
function getPersonScore(arr, id) {
const filtered = id ? arr.filter(e => e.personId === id) : arr;
return filtered.reduce((a, b) => {
if (Array.isArray(b.scores)) a += getPersonScore(b.scores);
return a + (b.score || 0);
}, 0);
}
console.log(getPersonScore(startingArray, 1));
<script>
const startingArray = [
{
id: 1,
personId: 1,
scores: [
{
id: 1,
title: 'Google',
score: 12
},
{
id: 2,
title: 'Bing',
score: 23
},
{
id: 3,
title: 'Facebook',
score: 34
}
],
score: null
},
{
id: 2,
personId: 1,
scores: null,
score: 123
},
{
id: 3,
personId: 2,
scores: [
{
id: 4,
title: 'Google',
score: 7
},
{
id: 5,
title: 'Bing',
score: 32
},
{
id: 6,
title: 'Facebook',
score: 9
}
],
score: null
},
{
id: 4,
personId: 3,
scores: null,
score: 106
},
{
id: 5,
personId: 3,
scores: [
{
id: 7,
title: 'Google',
score: 6
},
{
id: 8,
title: 'Bing',
score: 4
},
{
id: 9,
title: 'Facebook',
score: 3
}
],
score: 5
}
];
</script>
const personScores = (id, arr) => {
// error catching
if (!Array.isArray(arr)) {
throw new Error('Input array is not an array');
}
if (id >= arr.length) {
throw new Error(`Id ${id} out of array range (length ${arr.length})`);
}
if (!('scores' in arr[id])) {
throw new Error(`scores key missing in input array`);
}
if (!Array.isArray(arr[id].scores)) {
throw new Error(`Scores of input id ${id} not an array`);
}
// iterate scores array of startingArray[id], defaultValue of sum is 0
return arr[id].scores.reduce((sum, scoreObj) => {
if ('score' in scoreObj && !isNaN(parseInt(scoreObj.score, 10))) {
sum += parseInt(scoreObj.score, 10);
}
return sum;
}, 0); // defaultValue of the reducer
};
First find the person you want, then use reduce to add their scores, and finally add that to their score.
const startingArray = [{id:1,personId:1,scores:[{id:1,title:'Google',score:12},{id:2,title:'Bing',score:23},{id:3,title:'Facebook',score:34}],score:null},{id:2,personId:1,scores:null,score:123},{id:3,personId:2,scores:[{id:4,title:'Google',score:7},{id:5,title:'Bing',score:32},{id:6,title:'Facebook',score:9}],score:null},{id:4,personId:3,scores:null,score:106},{id:5,personId:3,scores:[{id:7,title:'Google',score:6},{id:8,title:'Bing',score:4},{id:9,title:'Facebook',score:3}],score:5}];
const personScores = id => {
let { scores, score } = startingArray.find(({ personId }) => personId);
let finalScore = 0;
if (score && score.length) finalScore += (Array.isArray(score) ? score.reduce((a, { score }) => a + score, 0) : score);
if (scores && scores.length) finalScore += (Array.isArray(scores) ? scores.reduce((a, { score }) => a + score, 0) : scores);
return finalScore;
};
console.log(personScores(1));
console.log(personScores(3));
(The above code checks both score and scores to see whether they are arrays or not, and performs the appropriate logic accordingly)

Moving data from 2 arrays of objects into a third array

so assume i have 2 arrays of objects...
let orders = [
{ id: 1, itemName: 'Peaches', amount: 2 },
{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }
];
let items = [
{ id: 1, name: 'Peaches', qty: 10 },
{ id: 2, name: 'Mangoes', qty: 3 }
];
and i want to find the list of orders for every item and put them in an array called linkedOrders, I tried the below code:
let linkedOrders = _.map(items, item => _.where(orders, { name: item.name }));
console.log(linkedOrders);
This is what I am getting:
[{ id: 1, itemName: 'Peaches', amount: 2 }],
[{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }]
but I want something like this:
[{'Peaches': [
{ id: 1, itemName: 'Peaches', amount: 2 }
],
'Mangoes': [
{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }
]],
Any help would be appreciated thanks.
You can simply do that using Array reduce method.
const result = items.reduce((result, item) => {
result.push({
[item.name]: orders.filter((order) => order.itemName === item.name)
});
return result;
}, []);
For more information on reduce, check Array​.prototype​.reduce()

Filter and combine fields from 2 arrays with ES6 JavaScript?

I have 2 arrays, one of pizza details and the other is an order state. The id field is what links them. So in this example, the order has 2 x Pepperoni and 3 x Margarita.
const pizzaContent = [
{
id: 0,
name: 'Pepperoni',
price: 20,
hot: true,
stockQuantity: 3
},
{
id: 1,
name: 'Margarita',
price: 25,
stockQuantity: 3
},
{
id: 2,
name: 'Hawaiian',
price: 15,
stockQuantity: 0
}
];
const orders = [{
id: 0,
quantity: 2
},{
id: 1,
quantity: 3
}];
I'm trying to create a new array which has the quantity from orders and the fields from pizzaContent. Any pizzas which aren't in the order shouldn't be part of this array.
I've gotten close with the following:
const pizzasInOrder = this.props.orders.map(order => {
return (
{
quantity: order.quantity,
pizza: this.props.pizzas.find(pizza => {
return (
order.id === pizza.id
);
})
}
)
});
However, the result is:
pizzasInOrder = [
{
pizza: {id: 0, name: "Pepperoni", price: 20, hot: true, stockQuantity: 3},
quantity:2
},
{
pizza: {id: 1, name: "Margarita", price: 25, stockQuantity: 3},
quantity:3
}
]
But what I need is:
pizzasInOrder = [
{
id: 0, name: "Pepperoni", price: 20, hot: true, stockQuantity: 3, quantity: 2
},
{
id: 1, name: "Margarita", price: 25, stockQuantity: 3, quantity: 3
}
]
Use Object.assign and no extra keys
const pizzasInOrder = this.props.orders.map(order =>
Object.assign({quantity: order.quantity},
this.props.pizzas.find(pizza => order.id === pizza.id))
);
You can use Object.assign() to merge objects into one.
example..
const pizzaContent = [
{
id: 0,
name: 'Peperoni',
price: 20,
hot: true,
stockQuantity: 3
},
{
id: 1,
name: 'Margarita',
price: 25,
stockQuantity: 3
},
{
id: 2,
name: 'Hawian',
price: 15,
stockQuantity: 0
}
];
const orders = [{
id: 0,
quantity: 2
},{
id: 1,
quantity: 3
}];
let pizzasInOrder = orders.map((order) => {
return Object.assign(order,
pizzaContent.find(pizza => order.id === pizza.id));
});
console.log(pizzasInOrder);

Categories