How to combine/merge/intersect two arrays of objects in one loop? - javascript

This code works, but I feel there must be a better way without having to use Array.find() twice.
const people = [
{ id: 0, age: 99 },
{ id: 1, age: 54 },
{ id: 2, age: 54 }
];
const roles = [
{ pId: 0, responsabilites: ['make money'] },
{ pId: 1, responsabilites: ['make money', 'complain'] },
{ pId: 4, responsabilites: ['make the world a better place', 'sarcasmm'] },
];
let roomsAndOrders = people.filter(p => {
return roles.find(r => r.pId === p.id);
});
roomsAndOrders = roomsAndOrders.map(p => {
let r = roles.find(r => r.pId === p.id);
return { ...r, ...p };
});
console.log(roomsAndOrders);

Just use one .map. Your original filter doesn't make much sense - filter filters out elements from an array that you don't want, but it doesn't change the elements. You're returning objects from your filter function, and objects are truthy, so the filter doesn't actually do anything.
Edit: Or just map the other way around - map from roles to people all at once, rather than mapping from people to roles.
const people = [
{ id: 0, age: 99 },
{ id: 1, age: 54 },
{ id: 2, age: 54 }
];
const roles = [
{ pId: 0, responsabilites: ['make money'] },
{ pId: 1, responsabilites: ['make money', 'complain'] },
];
const roomsAndOrders = roles.map(role => {
const person = people.find(({ id }) => role.pId === id);
return { ...role, ...person };
});
console.log(roomsAndOrders);
To only include objects whose IDs are in both arrays, you will have to use .reduce instead, since map always returns the same number of elements as in the original array:
const people = [
{ id: 0, age: 99 },
{ id: 1, age: 54 },
{ id: 2, age: 54 }
];
const roles = [
{ pId: 0, responsabilites: ['make money'] },
{ pId: 1, responsabilites: ['make money', 'complain'] },
{ pId: 4, responsabilites: ['make the world a better place', 'sarcasmm'] },
];
const roomsAndOrders = roles.reduce((accum, role) => {
const person = people.find(({ id }) => role.pId === id);
if (person) accum.push({ ...role, ...person });
return accum;
}, []);
console.log(roomsAndOrders);

You could do this in O(n) using a hashtable:
const result = [], hash = {};
for(const person of people)
result.push(hash[person.id] = {...person});
for(const role of roles)
if(hash[role.pId])
Object.assign(hash[role.pId], role);

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)

reduce array of array into a flat array of object

I'm stuck at transforming a data structure:
let d = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 }
]
];
d = d.reduce((accum, o) => {
}, [])
How can I produce this?
[{name: 'no 1', score: [7000, 8500]}, {name: 'no 2', score: [10000, 6500]}]
Here is one way to do it with simple reduce,
const result = d.flat().reduce((acc: {name: string, score: number[]}[], curr) => {
const { no, score } = curr;
let item = acc.find(a => a.name === `no ${no}`);
if (!item) {
item = { name: `no ${no}`, score: []};
acc.push(item);
}
item.score.push(score);
return acc;
}, []);
console.log(result)
let d = [{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[ { no: 1, score: 8500 },
{ no: 2, score: 6500 }]]
const result=d.flat().reduce((acc,curr)=>{
if(acc[curr.no]){
acc[curr.no].score.push(curr.score)
} else {
const keys=Object.keys(curr)
acc[curr.no]={ name: keys[0]+ ' '+curr.no, score:[curr.score]}
}
return acc;
},{})
console.log(Object.values(result))
Array.prototype.flat() call before the actual grouping, then use reduce function create the result.
let d = [{
no: 1,
score: 7000
},
{
no: 2,
score: 10000
},
[{
no: 1,
score: 8500
},
{
no: 2,
score: 6500
}
]
]
const result = d.flat().reduce((result, element) => {
const key = element.no;
if (!result[key]) {
result[key] = {
name: `no ${key}`,
score: []
}
}
result[key].score.push(element.score);
return result;
}, {})
console.log(Object.values(result))
Here's a sleek functional solution for typescript, since you included the tag-
const arr = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 },
],
];
const result = Object.entries(
arr.flat().reduce((accum: { [key: number]: number[] }, el: { no: number; score: number }) => {
accum[el.no] = (accum[el.no] ?? []).concat(el.score);
return accum;
}, {})
).map(([num, scores]) => ({ no: Number(num), scores: scores }));
console.log(result);
Result-
[
{ no: 1, scores: [ 7000, 8500 ] },
{ no: 2, scores: [ 10000, 6500 ] }
]
This flattens the inner arrays first using Array.prototype.flat. Then it uses reduce to construct an object that has the no values as keys and score values as an array of values.
In the end, the reduce results in { 1: [7000, 8500], 2: [10000, 6500] } - turn that into entries using Object.entries to get [['1', [7000, 8500]], ['2', [10000, 6500]]]
Finally, map over the entries to turn the ['1', [7000, 8500]] format into { no: 1, scores: [ 7000, 8500 ] } format and you're done!
You could take a dynamic approach which groups by the given key and takes all other properties for a new array.
const
groupBy = key => (r, value) => {
if (Array.isArray(value)) return value.reduce(group, r);
const { [key]: _, ...o } = value;
Object.entries(o).forEach(([k, v]) => ((r[_] ??= { [key]: _ })[k] ??= []).push(v));
return r;
}
data = [{ no: 1, score: 7000 }, { no: 2, score: 10000 }, [{ no: 1, score: 8500 }, { no: 2, score: 6500 }]],
group = groupBy('no'),
result = Object.values(data.reduce(group, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.prototype.flat()
or if you have any array like,
let d = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 }
]
];
and then use d.flat()
Try this:
let d = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 }
]
]
const reducer = (arr, start = []) => arr.reduce((acc, next) => {
if (Array.isArray(next)) return reducer(next, acc);
for (const value of acc) {
if (value.name === `no ${next.no}`) {
value.score = [...value.score, next.score];
return acc;
}
}
return [...acc, {
name: `no ${next.no}`,
score: [next.score]
}];
}, start);
console.log(reducer(d));
In your case, you not only need to flatten the list, but also group by no property.
For flatting, you can use Array.prototype.flat(). It's quite a new feature, so if you don't use polyfills, you probably can't use it. So you can check for alternative implementations.
For grouping, you can reduce to the object where the key is no property. Note, that if multiple no properties exist, you need to save an array of all score values.
Example:
const d = [{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[ { no: 1, score: 8500 },
{ no: 2, score: 6500 }]]
const grouped = d.flat().reduce((prev, cur) => {
if (cur.no in prev) {
prev[cur.no].score.push(cur.score)
} else {
prev[cur.no] = {
name: 'no ' + cur.no,
score: [cur.score]
}
}
return prev
}, {})
console.log(Object.values(grouped))
In the example we use modifications. It is possible to do it without modifications - return a new copy during each reduction iteration. However, depending on array size, there can be performance issues. Also, it's safe to do modifications, because we create a new object in this case.

What happen when i run array.some in array filter

I just learned a new trick to find the same object in 2 array object, it works very well. it uses array.filter and array.some, as code bellow, but I don't understand how filter() can run when some() will return true or false.
const similarity = (arr, values) => arr.filter(item => values.some(m => (m.id === item.id) && (m.name === item.name)));
my input:
let arr1 = [
{
id: 1,
name: "kiet"
},
{
id: 2,
name: 'phan'
},
{
id: 3,
name: 'tuan'
}]
let arr2 = [
{
id: 1,
name: "kiet"
},
{
id: 2,
name: 'haha'
},
{
id: 5,
name: 'tuan'
}
]
my result :
[ { id: 1, name: 'kiet' } ]
You are Filtering the array, then passing this condition inside the some
(m.id === item.id) && (m.name === item.name)
If the id of the second array (here, m is the second array's objects) is equal to the first array's id (here, item is the array's objects) and the name is equal to the first array's name property. If yes then it will return true. So the filter gets a true and if is does, it will return that particular object for which it gets a true
let arr1 = [{ id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan'}]
let arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' } ]
const similarity = (arr, values) => arr.filter(item => values.some(m => (m.id === item.id) && (m.name === item.name)));
console.log(similarity(arr1,arr2))
You get the elements from the array which have the same properties id/name as the values array.
Array#filter needs a (kind of) boolean value and if true or truthy, then the item is taken to the new array.
Array#some checks an item and if the callback returns a truthy value, then it returns true, if not then false.
const
similarity = (array, values) => array.filter(item => values.some(m => m.id === item.id && m.name === item.name)),
arr1 = [ { id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan' }],
arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' }],
result = similarity(arr1, arr2);
console.log(result);
For larger data sets, you could take a Set, this is iterated once with a combined value and checked against for filtering.
const
similarity = (array, values) => {
const
getKey = ({ id, name }) => [id, name].join('|'),
keys = new Set(values.map(getKey));
return array.filter(o => keys.has(getKey(o)));
},
arr1 = [ { id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan' }],
arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' }],
result = similarity(arr1, arr2);
console.log(result);
This is a good technique if you want to check for array intersection.
const vowels = [...'AEIOU'];
const isVowel = x => vowels.some(v => v === x);
const hasVowel = string => [...string].filter(isVowel).length > 0;

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