3

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
      }
    ]
  }
]

1 Answer 1

1

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);

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, you are amazing! That's exactly what I wanted however I just want one more thing which is that each value of ranking should be in an array of arrays like here you can see below ; { ranking: [['Death'], ['Breaker of Sky'], ['Slayer of Mountain'], ['Balthromaw']], count: 2 } How can I do it? Thanks in advance!
Oh, I missed that requirement. I'd keep it 1-d, but if you need those 1-element inner arrays for some reason it's pretty simple: use ranking: v.map(e => [e]) inside the final map instead of ranking: v.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.