Reputation: 59
I am trying to construct an array of object from another array and object.
let assets = [
{id: '1', count: 1, skills: ["teach", "play"]},
{id: '2', count: 1, skills: ["write", "surf"]},
{id: '3', count: 2, skills: ["run"]},
{id: '4', count: 3, skills: ["teach", "run", "hike"]}
]
And then I take all the unique skills available above.
let uniqueSkills = ["teach", "play", "write", "surf", "run", "hike"]
Every element in uniqueSkills array must be a key in an object. And if that key is present in assets array, I want to store that particular object's id
and count
.
I am expecting my final object to be something like this which I will have to use to plot a graph.
{
teach: [{id: 1, count: 1},{id: 4, count: 3}],
play: [{id: 1, count: 1}],
write: [{id: 2, count: 1}],
surf: [{id: 2, count: 1}],
run: [{id: 3, count: 2}, {id: 4, count: 3}],
hike: [{id: 4, count: 3}]
}
Upvotes: 0
Views: 117
Reputation: 2569
Well, there is no short way to get it done. Just a simple algorithm. Here is a solution that works.
let assets = [
{ id: '1', count: 1, skills: ["teach", "play"] },
{ id: '2', count: 1, skills: ["write", "surf"] },
{ id: '3', count: 2, skills: ["run"] },
{ id: '4', count: 3, skills: ["teach", "run", "hike"] }
];
// Filter unique keys into an array
let skillKeys = [];
assets.forEach(element => {
skillKeys = [...new Set(skillKeys.concat(element['skills']))];
});
// Final refined result
let refined = {};
let temp = [];
skillKeys.forEach(sKey => {
temp = [];
assets.forEach(a => {
if (a['skills'].includes(sKey)) {
temp.push({ id: a['id'], count: a['count'] });
}
})
refined[sKey] = temp;
});
console.log(refined);
Upvotes: 0
Reputation: 7446
Here is a solution without using reduce
that relies on function generators instead and uses .filter
to acquire the desired filtered values.
below code will iterate through all the unique skills and, for each of them, will build up an object.
It's not meant to be more efficient or whatever, it's just another way of accomplishing the task and is meant to be elastic and easy to maintain. Moreover, it may also be possible to don't rely on any array prototype at all.
Below approach allows for further object transformations if needed.
let assets = [
{id: '1', count: 1, skills: ["teach", "play"]},
{id: '2', count: 1, skills: ["write", "surf"]},
{id: '3', count: 2, skills: ["run"]},
{id: '4', count: 3, skills: ["teach", "run", "hike"]}
];
let uniqueSkills = ["teach", "play", "write", "surf", "run", "hike"];
// Aggregates the source results by looking for uniqueSkills in its skills property.
function* aggregateByUniqueSkills(source, uniqueSkills) {
// loop each uniques skill.
for (var skill of [...new Set(uniqueSkills)]) { // <-- new Set ensures there are no duplicates.
const skillSet = {[skill]: []}; // <-- define a new object with a key and a default empty array value.
for ({id, count} of source.filter(i => i.skills && i.skills.indexOf(skill) > -1)) { // acquire all the elements in the source whose skills contains the current skill.
skillSet[skill].push({id, count}); // push the value.
}
yield skillSet; // yield the current result.
}
}
console.log(Object.assign({}, ...aggregateByUniqueSkills(assets, uniqueSkills)));
Upvotes: 1
Reputation: 1721
let finalObj = {};
uniqueSkills.map( (skill) => {
finalObj[skill] = assets.filter( asset => asset.skills.includes(skill) )
.map( asset => ({id: asset.id, count: asset.count}) );
})
This is a bit complicated, so let me break it up for you:
going through all uniqueSkills with Array.map()
.
for each skill we create a property with the skill name inside our final object.
filter the assets array to remove any objects that do not include that skill in their skills
property.
on the array which comes back from filter- return an array which includes only the id
and count
propperties, and put that array inside the property created at step 2.
for more info on array functions: Array.map(), Array.filter().
Hope this helps :)
Upvotes: 1
Reputation: 35259
You could reduce
the array. Destructure the parameter to get skills
and rest
of the properties separately. Then loop through skills
and add/update the key in the accumulator
const assets=[{id:'1',count:1,skills:["teach","play"]},{id:'2',count:1,skills:["write","surf"]},{id:'3',count:2,skills:["run"]},{id:'4',count:3,skills:["teach","run","hike"]}]
const output = assets.reduce((acc, { skills, ...rest }) => {
skills.forEach(s => {
acc[s] = acc[s] || [];
acc[s].push(rest)
})
return acc;
}, {})
console.log(output)
(Check the browser's console for the actual output. Snippet isn't displaying it properly)
Upvotes: 3