Search & Update properties of an object nested in a Javascript Array - javascript

Say I have an array of nested objects like
let vendors = [
{
v_id: 'red',
count: 2,
},
{
v_id: 'blue',
count: 3,
},
{
v_id: 'green',
count: 1,
},
];
And another object "foo" with many properties, one of which is "v_id".
Based on the value of foo.v_id, I want to either update the counts in the "vendors" array or add a new object to "vendors".
If foo.v_id matches one of those in the "vendors" array, the corresponding count increases by 1.
Example:
let foo = {
user_type: 'Other',
v_id: 'blue'
};
Then "vendors" would become:
[
{
v_id: 'red',
count: 2,
},
{
v_id: 'blue',
count: 4,
},
{
v_id: 'green',
count: 1,
},
];
Else, if there is no match of v_id, a new object is added to the "vendors" array with the corresponding v_id & count = 1.
Example:
let foo = {
user_type: 'client',
v_id: 'yellow',
};
Then "vendors" would become:
[
{
v_id: 'red',
count: 2,
},
{
v_id: 'blue',
count: 3,
},
{
v_id: 'green',
count: 1,
},
{
v_id: 'yellow',
count: 1,
},
];
How can I efficiently & elegantly do this in Javascript? I know I can use .filter() to get the specific object in "vendors" that has to be updated but how would I update the "vendors" array itself?

Use Array.find() to search for the object in the array by v_id. If the object is in the array, increment count. If not push a new object:
const vendors = [{"v_id":"red","count":2},{"v_id":"blue","count":3},{"v_id":"green","count":1}];
const foo1 = { user_type: 'Other', v_id: 'blue' };
const foo2 = { user_type: 'client', v_id: 'yellow' };
const addUpdate = (arr, obj) => {
const current = arr.find((o) => o.v_id === obj.v_id);
if(current) current.count += 1;
else arr.push({
v_id: obj.v_id,
count: 1
});
};
addUpdate(vendors, foo1);
addUpdate(vendors, foo2);
console.log(vendors);

function upsert(arr, obj){
const index = arr.findIndex(item => item.v_id === obj.v_id);
index === -1 ? arr.push({v_id: obj.v_id, count: 1}) : arr[index].count++
}
let vendors = [{v_id: 'red',count: 2}, {v_id: 'blue',count: 3}, {v_id: 'green',count: 1}];
let foo1 = {user_type: 'Other', v_id: 'blue'};
let foo2 = {user_type: 'client', v_id: 'yellow'};
upsert(vendors, foo1);
upsert(vendors, foo2);
console.log(vendors);

Related

Naive approach to summarize array of objects?

I faced a challenge where I needed to summarize an array of objects by the object's keys. I found a solution, but I can't shake off the feeling, that my approach is pretty naive:
const objArr = [
{ id: 1, val: "🍊" },
{ id: 1, val: "🍇" },
{ id: 1, val: "🍎" },
{ id: 2, val: "🥦" },
{ id: 2, val: "🌽" },
{ id: 2, val: "🌶" },
];
let tempArr = [];
let uniqueIdArr = [];
let sortedArr = [];
objArr.forEach((obj) => {
tempArr.push(obj.id);
uniqueIdArr = [...new Set(tempArr)];
});
uniqueIdArr.forEach((uniqueId) => {
let arr = [];
objArr.forEach((obj) => {
if (obj.id == uniqueId) {
arr.push(obj.val);
}
});
sortedArr.push({
id: uniqueId,
vals: arr,
});
});
console.log(sortedArr);
// Output: [{ id: 1, vals: [ '🍊', '🍇', '🍎' ] }, { id: 2, vals: [ '🥦', '🌽', '🌶' ] }]
Maybe there is something I don't know about JavaScript's array methods yet? Is this approach totally wrong? Is there another way, so that I could reduce the code and make it more elegant?
So many questions...
Any hint or explanation would be much appreciated. 🙈
Thanks in advance
J.
you can use Array.prototype.reduce to make your code bit shorter:
const objArr = [
{ id: 1, val: "🍊" },
{ id: 1, val: "🍇" },
{ id: 1, val: "🍎" },
{ id: 2, val: "🥦" },
{ id: 2, val: "🌽" },
{ id: 2, val: "🌶" },
];
let result = objArr.reduce((acc,e) => {
let idx = acc.findIndex(s => s.id === e.id)
if(idx > -1){
acc[idx].vals.push(e.val)
}
else{
acc.push({id:e.id,vals:[e.val]})
}
return acc
},[])
console.log(result)
Your ideas are good and explicit, but far from being optimal.
const objArr = [
{ id: 1, val: "🍊" },
{ id: 1, val: "🍇" },
{ id: 1, val: "🍎" },
{ id: 2, val: "🥦" },
{ id: 2, val: "🌽" },
{ id: 2, val: "🌶" },
];
const idValMap = new Map();
objArr.forEach(o=>{
let vals = idValMap.get(o.id);
if(!vals){
vals = [];
idValMap.set(o.id,vals);
}
vals.push(o.val);
});
console.log(Array.from(idValMap.entries()));
You can do most of it in just one loop. Take the key, check if you saw it already, if not initialize. That's it
This solution probably isn't much better but it is does use less code:
const objArr = [
{ id: 1, val: "🍊" },
{ id: 1, val: "🍇" },
{ id: 1, val: "🍎" },
{ id: 2, val: "🥦" },
{ id: 2, val: "🌽" },
{ id: 2, val: "🌶" },
];
let sortedArr = [];
objArr.forEach((item) => {
const exists = sortedArr.filter(i => i.id === item.id).length; // Check to see if we've already added an item with this ID
if (!exists) {
const matches = objArr.filter(i => i.id == item.id); // get all items with this ID
sortedArr.push({
id: item.id,
vals: matches.map(m => m.val) // We only care about the val property
});
}
});
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter for informationa bout .map() and .filter() respectively

Compare two arrays of objects, and remove if object value is equal

I've tried modifying some of the similar solutions on here but I keep getting stuck, I believe I have part of this figured out however, the main caveat is that:
Some of the objects have extra keys, which renders my object comparison logic useless.
I am trying to compare two arrays of objects. One array is the original array, and the other array contains the items I want deleted from the original array. However there's one extra issue in that the second array contains extra keys, so my comparison logic doesn't work.
An example would make this easier, let's say I have the following two arrays:
const originalArray = [{id: 1, name: "darnell"}, {id: 2, name: "funboi"},
{id: 3, name: "jackson5"}, {id: 4, name: "zelensky"}];
const itemsToBeRemoved = [{id: 2, name: "funboi", extraProperty: "something"},
{id: 4, name: "zelensky", extraProperty: "somethingelse"}];
after running the logic, my final output should be this array:
[{id: 1, name: "darnell"}, {id: 3, name: "jackson5"}]
And here's the current code / logic that I have, which compares but doesn't handle the extra keys. How should I handle this? Thank you in advance.
const prepareArray = (arr) => {
return arr.map((el) => {
if (typeof el === "object" && el !== null) {
return JSON.stringify(el);
} else {
return el;
}
});
};
const convertJSON = (arr) => {
return arr.map((el) => {
return JSON.parse(el);
});
};
const compareArrays = (arr1, arr2) => {
const currentArray = [...prepareArray(arr1)];
const deletedItems = [...prepareArray(arr2)];
const compared = currentArray.filter((el) => deletedItems.indexOf(el) === -1);
return convertJSON(compared);
};
How about using filter and some? You can extend the filter condition on select properties using &&.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
console.log(
originalArray.filter(item => !itemsToBeRemoved.some(itemToBeRemoved => itemToBeRemoved.id === item.id))
)
Or you can generalise it as well.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
function filterIfSubset(originalArray, itemsToBeRemoved) {
const filteredArray = [];
for (let i = 0; i < originalArray.length; i++) {
let isSubset = false;
for (let j = 0; j < itemsToBeRemoved.length; j++) {
// check if whole object is a subset of the object in itemsToBeRemoved
if (Object.keys(originalArray[i]).every(key => originalArray[i][key] === itemsToBeRemoved[j][key])) {
isSubset = true;
}
}
if (!isSubset) {
filteredArray.push(originalArray[i]);
}
}
return filteredArray;
}
console.log(filterIfSubset(originalArray, itemsToBeRemoved));
Another simpler variation of the second approach:
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
const removeSubsetObjectsIfExists = (originalArray, itemsToBeRemoved) => {
return originalArray.filter(item => {
const isSubset = itemsToBeRemoved.some(itemToBeRemoved => {
return Object.keys(item).every(key => {
return item[key] === itemToBeRemoved[key];
});
});
return !isSubset;
});
}
console.log(removeSubsetObjectsIfExists(originalArray, itemsToBeRemoved));
The example below is a reusable function, the third parameter is the key to which you compare values from both arrays.
Details are commented in example
const arr=[{id:1,name:"darnell"},{id:2,name:"funboi"},{id:3,name:"jackson5"},{id:4,name:"zelensky"}],del=[{id:2,name:"funboi",extraProperty:"something"},{id:4,name:"zelensky",extraProperty:"somethingelse"}];
/** Compare arrayA vs. delArray by a given key's value.
--- ex. key = 'id'
**/
function deleteByKey(arrayA, delArray, key) {
/* Get an array of only the values of the given key from delArray
--- ex. delList = [1, 2, 3, 4]
*/
const delList = delArray.map(obj => obj[key]);
/* On every object of arrayA compare delList values vs
current object's key's value
--- ex. current obj[id] = 2
--- [1, 2, 3, 4].includes(obj[id])
Any match returns an empty array and non-matches are returned
in it's own array.
--- ex. ? [] : [obj]
The final return is a flattened array of the non-matching objects
*/
return arrayA.flatMap(obj => delList.includes(obj[key]) ? [] : [obj]);
};
console.log(deleteByKey(arr, del, 'id'));
let ff = [{ id: 1, name: 'darnell' }, { id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' }]
let cc = [{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' }]
let ar = []
let out = []
const result = ff.filter(function(i){
ar.push(i.id)
cc.forEach(function(k){
out.push(k.id)
})
if(!out.includes(i.id)){
// console.log(i.id, i)
return i
}
})
console.log(result)

Filter an array of objects by a N-length map of props with 3 states for each prop

I'm doing a complex filter, for which I have an initial list of objects with unique ids.
And a map with N properties with a list of corresponding object copies for each prop; and three states for each prop: idle: 0, show: 1, hide: 2.
For now I managed to do this with lodash's differenceBy and intersectionBy. My filter function takes in an array of objects and mutates the array, by checking and filtering the array with every map prop.
But concerning efficiency and growing number of complexity, should this kind of problem be solved differently?
For example:
If this filter is applied to a big array of hex colors (length 100, 1000 or more)
colors [1, 2, 3, ...1000]
And prop map has a growing number of props, like tags, by which a user can mark colors and show/hide them on filter. Or at some point new prop states will be added.
prop1 0, 1, 2, ...10
prop2 0, 1, 2, ...10
prop3 0, 1, 2, ...10
...
prop100 0, 1, 2, ...10
Should this kind of problem be solved via graph or matrix algorithms or some other method respectively? And, if yes, to what I should look into?
My code for optimisation and efficiency concerns:
const propMap = [
{ name: 'prop1', value: 0, items: [] },
{ name: 'prop2', value: 1, items: [ { id: 1}, { id: 2} ] },
{ name: 'propN', value: 2, items: [ { id: 2} ] },
];
const someArr = [
{ id: 1}, { id: 2}, { id: 3}, { id: 4},{ id: 5},
]
function filterByPropMap (arr) {
// Filter hidden from array
propMap.forEach(prop => {
if (prop.value === 2) {
arr = _.differenceBy(arr, prop.items, 'id');
}
});
// Filter intersecting objects to show
propMap.forEach(prop => {
if (prop.value === 1) {
arr = _.intersectionBy(arr, prop.items, 'id');
}
});
return [...arr];
}
console.log(filterByPropMap(someArr));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
In general, you very often don't need Lodash. Consider the following, which uses only vanilla ES6.
It returns { id: 1 } twice, because it looks like the deduplication is an unintended side effect of your code. At least you never do so explicitly.
const propMap = [
{ name: 'prop1', value: 0, items: [] },
{ name: 'prop2', value: 1, items: [ { id: 1}, { id: 2} ] },
{ name: 'propN', value: 2, items: [ { id: 2} ] },
];
const someArr = [
{ id: 1}, { id: 2}, { id: 3}, { id: 4},{ id: 1},
];
function filterByPropMap(arr) {
const hiddenItems = propMap
.filter(p => p.value === 2)
.map(p => p.items)
.flat();
const intersectingItems = propMap
.filter(p => p.value === 1)
.map(p => p.items)
.flat();
const isEqual = (a, b) => a.id === b.id;
return arr
.filter(v => !hiddenItems.some(h => isEqual(h, v)) &&
intersectingItems.some(i => isEqual(i, v)));
}
console.log(filterByPropMap(someArr));

Immutable.js algorithm: List.update_or_add(item)

I want to concatenate 2 lists in immutable.js.
Both lists have this structure: { id, value }
The algorithm concatenate should do this:
If an ID exists in both list1 and list2 take the value from list2.
let list1 = [
{ id: 1, value: 'foo' },
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
]
let list2 = [
{ id: 1, value: 'quux' }, // id 1 exists in list1
{ id: 4, value: 'asd' },
]
let result = [
{ id: 1, value: 'quux' }, // from list 2
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
{ id: 4, value: 'asd' },
]
If Immutable.js has this functionality with another type (eg. Dictionary), I could also use that.
Algorithms for union
First you have to maintain two map with key as id and value as object then check for length of array which is of bigger size and pass the bigger size array with small size map to merged function there you can iterate over the array and check if it's exists in the map if yes then update the object and delete that row from map otherwise add the object into output. After the for loop complete check if map has element present then push all the values from map into output array and return;
index.js
const old = [
{ id: 1, value: 'foo' },
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
];
const newa = [
{ id: 1, value: 'quux' }, // update
{ id: 4, value: 'asd' }, // push
];
function merged(input,filterMap){
var output = [];
input.forEach(function(eachRow){
if(filterMap.hasOwnProperty(eachRow.id)){
output.push(Object.assign(eachRow,filterMap[eachRow.id]));
delete filterMap[eachRow.id];
}else{
output.push(eachRow);
}
});
if(Object.keys(filterMap).length > 0){
output = output.concat(Object.values(filterMap));
}
return output;
}
function parseData(first,second){
var mapFirst = {},
mapSecond = {};
var output = [];
first.forEach(function(eachRow){
mapFirst[eachRow.id] = eachRow;
});
second.forEach(function(eachRow){
mapSecond[eachRow.id] = eachRow;
});
if(first.length > second.length){
return merged(first,mapSecond);
}else{
return merged(second,mapFirst);
}
}
console.log(parseData(old,newa));
Working jsFiddle demo - https://jsfiddle.net/qz25hnmf/

How to transform multidimensional array into chart data with es6 array methods

I need to convert one array to in specific data format to display the chart.
chrat.js library require data in this format
dataset = [ { label: 'one', data: []},
{label: 'two', data: []}
];
and I receive the response data in another format in random order so need to change appropriately with the respective label.
here is my code and trial.
const dataset = [
{
detail: {
team: [
{ name: 'alpha', game: 1 },
{ name: 'beta', game: 1 },
{ name: 'gamma', game: 1 },
{ name: 'delta', game: 1 },
{ name: 'echo', game: 1 }
]
}
},
{
detail: {
team: [
{ name: 'alpha', game: 2 },
{ name: 'beta', game: 2 },
{ name: 'echo', game: 2 },
{ name: 'gamma', game: 2 },
{ name: 'delta', game: 2 }
]
}
},
{
detail: {
team: [
{ name: 'echo', game: 1 },
{ name: 'delta', game: 0 },
{ name: 'beta', game: 0 },
{ name: 'gamma', game: 0 },
{ name: 'alpha', game: 0 }
]
}
},
{
detail: {
team: [
{ name: 'delta', game: 0 },
{ name: 'echo', game: 0 },
{ name: 'beta', game: 0 },
{ name: 'gamma', game: 1 },
{ name: 'alpha', game: 0 }
]
}
},
{
detail: {
team: [
{ name: 'delta', game: 0 },
{ name: 'echo', game: 0 },
{ name: 'alpha', game: 2 },
{ name: 'gamma', game: 3 },
{ name: 'beta', game: 2 }
]
}
},
{
detail: {
team: [
{ name: 'delta', game: 0 },
{ name: 'echo', game: 1 },
{ name: 'beta', game: 0 },
{ name: 'gamma', game: 2 },
{ name: 'alpha', game: 0 }
]
}
}
];
const teams = dataset.map(ds => ds.detail.team);
let z = teams.map(element => {
return element.map(e => {
let p = {};
let n = e.name;
let c = e.game;
p[n] = c;
return p;
});
});
console.log('z', z);
let nt = [];
z.reduce((c, n, i, a) => {
let z1 = n.map((i) => {
console.log(i);
let entries = Object.entries(i);
return entries.map((e) => {
return { label: e[0], data: e[1] };
});
});
return z1;
}, [])
desired output:
[
{
label: 'alpha',
data: [1, 2, 0, 0, 2, 0]
},
{
label: 'beta',
data: [1, 2, 0, 0, 2, 0]
},
{
label: 'gamma',
data: [1, 2, 0, 1, 3, 2]
},
{
label: 'delta',
data: [ 1, 2, 0, 0, 0, 0]
},
{
label: 'echo',
data: [1, 2, 1, 0, 0, 1]
}
]
I lost somewhere in the array.reduce method to achieve the output.
I am preferably looking for a es6 solution
any help is appreciated.
So I'm going to leave your dataset the same but lets start from the ground up and create some code to step through your data set and get to the desired output.
First we need to de-nest the data:
dataset.map(d => d.detail.team)
Now that we have teams lets reduce them all to a single array
dataset
.map(object => object.detail.team)
.reduce((acc, team) => acc.concat(team))
Okay good now we have one big set of names and games. We can now make this pretty easily into a hash
dataset
.map(object => object.detail.team)
.reduce((acc, team) => acc.concat(team))
.reduce((acc, team) =>{
acc[team.name] = acc[team.name] || []
acc[team.name].push(team.game)
return acc
}, {})
Now we have a hash of names to games. Calling Object.entries on this hash will give us pairs of lables
Object.entries(
dataset
.map(object => object.detail.team)
.reduce((acc, team) => acc.concat(team))
.reduce((acc, team) =>{
acc[team.name] = acc[team.name] || []
acc[team.name].push(team.game)
return acc
}, {})
)
Now we can map over these pairs to construct the final object
Object.entries(
dataset
.map(object => object.detail.team)
.reduce((acc, team) => acc.concat(team), [])
.reduce((acc, team) =>{
acc[team.name] = acc[team.name] || []
acc[team.name].push(team.game)
return acc
}, {})
)
.map(([team, games]) => ({ team, games }))
The real trick now is how many of these steps can be combined?
Well most of them! We can reduce this to looping over each object, referencing manually since we know structure, and then looping over each individual team array and finally constructing our hash.
Object.entries(
dataset
.reduce((acc, object) =>{
object.detail.team.forEach(team =>{
acc[team.name] = acc[team.name] || []
acc[team.name].push(team.game)
})
return acc
}, {})
)
.map(([team, games]) => ({ team, games }))
Extra Notes
Arrow Functions
We used arrow functions in this example to adhere to the request of using ES6 as much as possible. More information on arrow functions can be found on the MDN. Basically though it's another way to declare a function
function test(value){ return console.log(value) }
// same as
let test = value => console.log(value)
function add(a, b){ return a + b)
// same as
let add = (a,b) => a + b
Note the Array.prototype.forEach()
Now you'll notice we used an Array.prototype.forEach() in the combined example to manipulate the accumulator. That sentence should say all we need to there but for clarification for those who might not know, forEach is to be used when you want no return value and only want side effects. In this situation it's faster than attempting to actually return something since we don't want the overhead of discarding a bunch of arrays we've made when the end goal is to only change the way the accumulator looks.
That funky array being passed to a function
Ah yes, destructuring. Again more information can be found on the MDN. Basically it lets us pull values out of Objects or Arrays we know the structure of in advance. Note: Example courtesy of MDN article
var a, b, rest;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]
({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20
// Stage 3 proposal
({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}
You can use Array.reduce(), to create a map and than use that map to get the desired output.
const dataset = [{detail:{team:[{name:'alpha',game:1},{name:'beta',game:1},{name:'gamma',game:1},{name:'delta',game:1},{name:'echo',game:1}]}},{detail:{team:[{name:'alpha',game:2},{name:'beta',game:2},{name:'echo',game:2},{name:'gamma',game:2},{name:'delta',game:2}]}},{detail:{team:[{name:'echo',game:1},{name:'delta',game:0},{name:'beta',game:0},{name:'gamma',game:0},{name:'alpha',game:0}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:0},{name:'beta',game:0},{name:'gamma',game:1},{name:'alpha',game:0}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:0},{name:'alpha',game:2},{name:'gamma',game:3},{name:'beta',game:2}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:1},{name:'beta',game:0},{name:'gamma',game:2},{name:'alpha',game:0}]}}];
var map = dataset.reduce((a,curr)=>{
curr.detail.team.forEach((e)=> (a[e.name]= (a[e.name] || [])).push(e.game));
return a;
}, {});
var result =[];
Object.keys(map).forEach((key)=>{
result.push({
"label" : key,
"data" : map[key]
});
});
console.log(result);
You can use reduce to make a flat array and then loop over to get the wanted format
const dataset = [{detail:{team:[{name:'alpha',game:1},{name:'beta',game:1},{name:'gamma',game:1},{name:'delta',game:1},{name:'echo',game:1}]}},{detail:{team:[{name:'alpha',game:2},{name:'beta',game:2},{name:'echo',game:2},{name:'gamma',game:2},{name:'delta',game:2}]}},{detail:{team:[{name:'echo',game:1},{name:'delta',game:0},{name:'beta',game:0},{name:'gamma',game:0},{name:'alpha',game:0}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:0},{name:'beta',game:0},{name:'gamma',game:1},{name:'alpha',game:0}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:0},{name:'alpha',game:2},{name:'gamma',game:3},{name:'beta',game:2}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:1},{name:'beta',game:0},{name:'gamma',game:2},{name:'alpha',game:0}]}}];
const flat = dataset.reduce( (a,b) => a.concat(b.detail.team), []);
let result = [];
for (let element of flat) {
let match = null;
for (let e of result) {
if (e.label === element.name) {
match = e;
}
}
if (match) {
match.data.push(element.game)
}
else {
result.push({
label : element.name,
data : [element.game]
});
}
}
console.log(result);
Another way: loop through the data set as it is, storing the results in a map dictionary-like object as well as in the array of results to be returned.
const dataset = [{detail:{team:[{name:'alpha',game:1},{name:'beta',game:1},{name:'gamma',game:1},{name:'delta',game:1},{name:'echo',game:1}]}},{detail:{team:[{name:'alpha',game:2},{name:'beta',game:2},{name:'echo',game:2},{name:'gamma',game:2},{name:'delta',game:2}]}},{detail:{team:[{name:'echo',game:1},{name:'delta',game:0},{name:'beta',game:0},{name:'gamma',game:0},{name:'alpha',game:0}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:0},{name:'beta',game:0},{name:'gamma',game:1},{name:'alpha',game:0}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:0},{name:'alpha',game:2},{name:'gamma',game:3},{name:'beta',game:2}]}},{detail:{team:[{name:'delta',game:0},{name:'echo',game:1},{name:'beta',game:0},{name:'gamma',game:2},{name:'alpha',game:0}]}}];
var result = [],
map = {};
dataset.forEach(a => {
a.detail.team.forEach(b => {
if (!(b.name in map)) {
map[b.name] = [];
result.push({
'label': b.name,
'data': map[b.name]
})
}
map[b.name].push(b.game);
});
});
console.log(result);
There's not much need to reduce or map any arrays here.

Categories