skip to Main Content

I am working with a large array in the following format:

// Sample data
const array = [
{country: "Germany", category: "A", value: 300},
{country: "Brazil", category: "A", value: 200},
{country: "Canada", category: "A", value: 200},
{country: "Germany", category: "B", value: 100},
{country: "Brazil", category: "B", value: 400},
{country: "Canada", category: "B", value: 500},
]

and I want to want to rank the values by category and value rank and potentially add an overall_rank without considering the category:

// Expected result
[
{country: "Germany", category: "A", value: 300, rank: 1, overall_rank: 2},
{country: "Brazil", category: "A", value: 200, rank: 2, overall_rank: 3},
{country: "Canada", category: "A", value: 200, rank: 2, overall_rank: 3},
{country: "Germany", category: "B", value: 100, rank: 3, overall_rank: 4},
{country: "Brazil", category: "B", value: 200, rank: 2, overall_rank: 3},
{country: "Canada", category: "B", value: 500, rank: 1, overall_rank: 1},
]

So far I got and overall_rank working, but I am struggling ranking the values by value and category:

var overall_rank = 1;
for (var i = 0; i < array.length; i++) {
  if (i > 0 && array[i].value < array[i - 1].value) {
    overall_rank++;
  }
    array[i].overall_rank = overall_rank;
}

How to move forward?

2

Answers


  1. To get the rank within the category, you can first use Object.groupBy to split the objects based on their category property, then sort each of the categories separately.

    const array = [
      {country: "Germany", category: "A", value: 300},
      {country: "Brazil", category: "A", value: 200},
      {country: "Canada", category: "A", value: 200},
      {country: "Germany", category: "B", value: 100},
      {country: "Brazil", category: "B", value: 400},
      {country: "Canada", category: "B", value: 500},
    ];
    for (const category of Object.values(Object.groupBy(array, o => o.category))) {
      let i = 0, prev;
      for (const o of category.sort((a, b) => b.value - a.value)) {
        o.rank = i += prev !== o.value;
        prev = o.value;
      }
    }
    console.log(array);
    Login or Signup to reply.
  2. You could sort each record and assign a unique rank:

    const arr = [
      { country: "Germany", category: "A", value: 300 },
      { country: "Brazil",  category: "A", value: 200 },
      { country: "Canada",  category: "A", value: 200 },
      { country: "Germany", category: "B", value: 100 },
      { country: "Brazil",  category: "B", value: 400 },
      { country: "Canada",  category: "B", value: 500 },
    ];
    
    const sorted = arr.toSorted((a, b) =>
      b.value - a.value ||
      a.category?.localeCompare(b.category) ||
      a.country?.localeCompare(b.country)) ||
      0;
    
    const result = arr.map((record) => ({
      ...record,
      overall_rank: sorted.indexOf(record) + 1
    }));
    
    console.log(...result.map(JSON.stringify));
    .as-console-wrapper { top: 0; max-height: 100% !important; }

    Or you could group by value:

    const arr = [
      { country: "Germany", category: "A", value: 300 },
      { country: "Brazil",  category: "A", value: 200 },
      { country: "Canada",  category: "A", value: 200 },
      { country: "Germany", category: "B", value: 100 },
      { country: "Brazil",  category: "B", value: 400 },
      { country: "Canada",  category: "B", value: 500 },
    ];
    
    const grouped = [...arr
      .reduce((acc, record, index) =>
        acc.set(record.value, [...(acc.get(record.value) ?? []), record]),
        new Map())
      .entries()]
      .sort(([a], [b]) => b - a);
      
    const result = arr.map((record) => ({
      ...record,
      overall_rank: grouped.findIndex(([value]) => value === record.value) + 1
    }));
    
    console.log(...result.map(JSON.stringify));
    .as-console-wrapper { top: 0; max-height: 100% !important; }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search