Search code examples
javascriptarraysecmascript-6javascript-objectsecmascript-2016

Compare all objects in array by value against each other


I am trying to determine which object in an array has a property value greater than the other objects but according to some condition.

I got an array of objects that represent staff and their line managers, if a person reports to a line manager equal to the rest but their Rank is higher I want to be able to reflect that on the property LineManager by adding 1. A good example is Tomas he has a Rank of 6 and reports to Peter but all the other people who also report to Peter have a Rank of 5.

I have managed to get the right output with this:

const data = [
  {
    Name: 'Peter',
    ReportsTo: '',
    LineManager: '',
    Rank: 4
  },
  {
    Name: 'Tom',
    ReportsTo: 'Peter',
    LineManager: 0,
    Rank: 5
  },
  {
    Name: 'Adam',
    ReportsTo: 'Peter',
    LineManager: 0,
    Rank: 5
  },
  {
    Name: 'Maria',
    ReportsTo: 'Peter',
    LineManager: 0,
    Rank: 5
  },
  {
    Name: 'Fiona',
    ReportsTo: 'Maria',
    LineManager: 0,
    Rank: 7
  },
  {
    Name: 'William',
    ReportsTo: 'Maria',
    LineManager: 0,
    Rank: 6
  },
  {
    Name: 'Tomas',
    ReportsTo: 'Peter',
    LineManager: 0,
    Rank: 6
  },
  {
    Name: 'Vicky',
    ReportsTo: 'Maria',
    LineManager: 0,
    Rank: 6
  }
]

const lineManager = data.find(({ ReportsTo }) => ReportsTo === '').Name

const rest = data.filter(item => (item.ReportsTo === lineManager))

const min = Math.min.apply( Math, rest.map(({ Rank }) => Rank ))

const final = data.map(item => {

  if (item.Rank > min && item.ReportsTo !== '' && item.ReportsTo === lineManager) {
    return {
      ...item,
      LineManager: 1
    }
  } else {
    return {
      ...item
    }
  }
})

console.log(final)

But how could I do it for deeper levels? For instance Fiona reports to Maria and has a Rank of 7 whereas William has a 6. Ideally Fiona will also have a LineManager: 1

Desired output would be this but the order is not relevant

[ 
  { Name: 'Peter', ReportsTo: '', LineManager: '', Rank: 4 }, 
  { Name: 'Tom', ReportsTo: 'Peter', LineManager: 0, Rank: 5 }, 
  { Name: 'Adam', ReportsTo: 'Peter', LineManager: 0, Rank: 5 }, 
  { Name: 'Maria', ReportsTo: 'Peter', LineManager: 0, Rank: 5 },
  { Name: 'Tomas', ReportsTo: 'Peter', LineManager: 1, Rank: 6 },  
  { Name: 'William', ReportsTo: 'Maria', LineManager: 0, Rank: 6 }, 
  { Name: 'Vicky', ReportsTo: 'Maria', LineManager: 0, Rank: 6 },
  { Name: 'Fiona', ReportsTo: 'Maria', LineManager: 1, Rank: 7 } 
] 

Solution

  • You could collect the minimum rank of all ReportsTo groups and map new object with incremented value if Rankis not the minimum of the group.

    const
        data = [{ Name: 'Peter', ReportsTo: '', LineManager: '', Rank: 4 }, { Name: 'Tom', ReportsTo: 'Peter', LineManager: 0, Rank: 5 }, { Name: 'Adam', ReportsTo: 'Peter', LineManager: 0, Rank: 5 }, { Name: 'Maria', ReportsTo: 'Peter', LineManager: 0, Rank: 5 }, { Name: 'Fiona', ReportsTo: 'Maria', LineManager: 0, Rank: 7 }, { Name: 'William', ReportsTo: 'Maria', LineManager: 0, Rank: 6 }, { Name: 'Tomas', ReportsTo: 'Peter', LineManager: 0, Rank: 6 }, { Name: 'Vicky', ReportsTo: 'Maria', LineManager: 0, Rank: 6 }],
        temp = data.reduce((r, o) => {
            r[o.ReportsTo] ??= Number.POSITIVE_INFINITY;
            if (r[o.ReportsTo] > o.Rank) r[o.ReportsTo] = o.Rank;
            return r;
        }, {}),
        result = data.map(o => ({ ...o, LineManager: temp[o.ReportsTo] === o.Rank
            ? o.LineManager
            : o.LineManager + 1
        }));
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }