kaleem
kaleem

Reputation: 59

All element of an array as key in an Object

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

Answers (4)

Adedoyin Akande
Adedoyin Akande

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

briosheje
briosheje

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

Gibor
Gibor

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:

  1. going through all uniqueSkills with Array.map().

  2. for each skill we create a property with the skill name inside our final object.

  3. filter the assets array to remove any objects that do not include that skill in their skills property.

  4. 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

adiga
adiga

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

Related Questions