Rasqo
Rasqo

Reputation: 41

Restructring array of objects

I've been struggling to restructure this particular object;

[
  {
    "userId": 1,
    "name": "Breaker of Sky",
    "rank": 1
  },
  {
    "userId": 1,
    "name": "Slayer of Mountain",
    "rank": 2
  },
  {
    "userId": 1,
    "name": "Balthromaw",
    "rank": 3
  },
  {
    "userId": 1,
    "name": "Death",
    "rank": -1
  },
  {
    "userId": 3,
    "name": "Breaker of Sky",
    "rank": 2
  },
  {
    "userId": 3,
    "name": "Slayer of Mountain",
    "rank": 3
  },
  {
    "userId": 3,
    "name": "Balthromaw",
    "rank": 1
  },
  {
    "userId": 3,
    "name": "Death",
    "rank": -1
  },
  {
    "userId": 4,
    "name": "Breaker of Sky",
    "rank": 3
  },
  {
    "userId": 4,
    "name": "Slayer of Mountain",
    "rank": 2
  },
  {
    "userId": 4,
    "name": "Balthromaw",
    "rank": 1
  },
  {
    "userId": 4,
    "name": "Death",
    "rank": -1
  },
  {
    "userId": 8,
    "name": "Breaker of Sky",
    "rank": 2
  },
  {
    "userId": 8,
    "name": "Slayer of Mountain",
    "rank": 3
  },
  {
    "userId": 8,
    "name": "Balthromaw",
    "rank": 4
  },
  {
    "userId": 8,
    "name": "Death",
    "rank": 1
  }
]

to make it look like this object below.

[
     { ranking: [['Death'], ['Breaker of Sky'], ['Slayer of Mountain'], ['Balthromaw']], count: 2 },
     { ranking: [['Death'], ['Balthromaw'], ['Breaker of Sky'], ['Slayer of Mountain']], count: 1 },
     { ranking: [['Death'], ['Balthromaw'], ['Slayer of Mountain'], ['Breaker of Sky']], count: 1 },
]

To simply explain the process, we first need to look for the userId's and group the names with ranks which have same userId's. Then it should line up the names by looking their ranks (-1 will always be at the first place). After that, we should look for the sequence, count it and write down. As an example, this sequence, [['Death'], ['Breaker of Sky'], ['Slayer of Mountain'], ['Balthromaw']] , has been found 2 times and therefore count is 2.

I would appreciate your help regarding this problem.

My code:

Here is what I have done so far. It's still missing the lining up part of the names by sorting ranks and also values of the ranking property are not arrays. Additionally, I couldn't do the count of sequence part too. Could you help me out with it?

const merged = data.reduce((r, { userId, ...rest }) => {
  const key = `${userId}`;
  r[key] = r[key] || { ranking: [] };
  r[key]['ranking'].push(rest);
  return r;
}, {});

const rankArray = Object.values(merged);
console.log(rankArray);

The output I get:

[
  {
    "ranking": [
      {
        "name": "Breaker of Sky",
        "rank": 1
      },
      {
        "name": "Slayer of Mountain",
        "rank": 2
      },
      {
        "name": "Balthromaw",
        "rank": 3
      },
      {
        "name": "Death",
        "rank": -1
      }
    ]
  },
  {
    "ranking": [
      {
        "name": "Breaker of Sky",
        "rank": 2
      },
      {
        "name": "Slayer of Mountain",
        "rank": 3
      },
      {
        "name": "Balthromaw",
        "rank": 1
      },
      {
        "name": "Death",
        "rank": -1
      }
    ]
  },
  {
    "ranking": [
      {
        "name": "Breaker of Sky",
        "rank": 3
      },
      {
        "name": "Slayer of Mountain",
        "rank": 2
      },
      {
        "name": "Balthromaw",
        "rank": 1
      },
      {
        "name": "Death",
        "rank": -1
      }
    ]
  },
  {
    "ranking": [
      {
        "name": "Breaker of Sky",
        "rank": 2
      },
      {
        "name": "Slayer of Mountain",
        "rank": 3
      },
      {
        "name": "Balthromaw",
        "rank": 4
      },
      {
        "name": "Death",
        "rank": 1
      }
    ]
  }
]

Upvotes: 3

Views: 62

Answers (1)

ggorlen
ggorlen

Reputation: 57115

There's a few steps to do:

  1. Group by userId, which you've already done.
  2. Sort the values for each user by rank.
  3. Build a map of counts for each array of ranking items. I'm assuming the ranking numbers don't matter, only the string names.
  4. Prune any extra elements with count > 1 in the map. (I think this is the logic you want; if it isn't, you can remove the delete byUser[userId] line)
  5. Iterate and attach the final counts to each object in the result array.

Here's one approach to all of this:

const data = [ { "userId": 1, "name": "Breaker of Sky", "rank": 1 }, { "userId": 1, "name": "Slayer of Mountain", "rank": 2 }, { "userId": 1, "name": "Balthromaw", "rank": 3 }, { "userId": 1, "name": "Death", "rank": -1 }, { "userId": 3, "name": "Breaker of Sky", "rank": 2 }, { "userId": 3, "name": "Slayer of Mountain", "rank": 3 }, { "userId": 3, "name": "Balthromaw", "rank": 1 }, { "userId": 3, "name": "Death", "rank": -1 }, { "userId": 4, "name": "Breaker of Sky", "rank": 3 }, { "userId": 4, "name": "Slayer of Mountain", "rank": 2 }, { "userId": 4, "name": "Balthromaw", "rank": 1 }, { "userId": 4, "name": "Death", "rank": -1 }, { "userId": 8, "name": "Breaker of Sky", "rank": 2 }, { "userId": 8, "name": "Slayer of Mountain", "rank": 3 }, { "userId": 8, "name": "Balthromaw", "rank": 4 }, { "userId": 8, "name": "Death", "rank": 1 } ];

const byUser = data.reduce((a, e) => {
  a[e.userId] = a[e.userId] || [];
  a[e.userId].push(e);
  return a;
}, {});
const counts = {};

for (const [userId, ranking] of Object.entries(byUser)) {
  ranking.sort((a, b) => a.rank - b.rank);
  byUser[userId] = ranking.map(e => e.name);
  const k = byUser[userId];
  counts[k] = ++counts[k] || 1;
  
  if (counts[k] > 1) {
    delete byUser[userId];
  }
}

const result = Object.values(byUser).map(v => ({
  ranking: v,
  count: counts[v],
}));
console.log(result);

Upvotes: 1

Related Questions