Search code examples
javascriptarraysecmascript-6arrow-functions

How to create a unique array of json objects with sort on a field?


How can I filler the below array on common id and output should have unique and latest history number

const input = [{
  "id": 134116,
  "user": "admin",
  "historyno": "134116-0"
}, {
  "id": 134132,
  "user": "admin",
  "historyno": "134132-0"
}, {
  "id": 134132,
  "user": "admin",
  "historyno": "134132-1"
}, {
  "id": 134133,
  "user": "admin",
  "historyno": "134133-0"
}, {
  "id": 134133,
  "user": "admin",
  "historyno": "134133-1"
}];

let output = [];
let tempId;

for (let i = 0; i < input.length; i++) {
  if (input[i].id === tempId) {
    //do nothing
  } else {
    output.push(input[i]);
    tempId = input[i].id;
  }
}

console.log(output);

Expected Output

[
  {
    "id": 134116,
    "user": "admin",
    "historyno": "134116-0"
  },
  {
    "id": 134132,
    "user": "admin",
    "historyno": "134132-1"
  },
  {
    "id": 134133,
    "user": "admin",
    "historyno": "134133-1"
  }
]

Solution

  • Reduce the array to a Map, using the id as key, and then convert back by spreading the Map.values() iterator to an array.

    This solution assumes that the array is presorted by history numbers:

    const input = [{"id":134116,"user":"admin","historyno":"134116-0"},{"id":134132,"user":"admin","historyno":"134132-0"},{"id":134132,"user":"admin","historyno":"134132-1"},{"id":134133,"user":"admin","historyno":"134133-0"},{"id":134133,"user":"admin","historyno":"134133-1"}];
    
    const output = [...input.reduce((r, o) => r.set(o.id, o), new Map).values()];
    
    console.log(output);

    This solution handles unsorted arrays by only replacing the current item in the map if historyno is greater:

    const input = [{"id":134116,"user":"admin","historyno":"134116-0"},{"id":134132,"user":"admin","historyno":"134132-0"},{"id":134132,"user":"admin","historyno":"134132-1"},{"id":134133,"user":"admin","historyno":"134133-0"},{"id":134133,"user":"admin","historyno":"134133-1"}];
    
    const getHistoryNo = ({ historyno }) => +historyno.split('-')[1];
    
    const output = [...input.reduce((r, o) => {
      const prev = r.get(o.id);
      
      if(!prev || getHistoryNo(o) > getHistoryNo(prev)) r.set(o.id, o);
      
      return r;
    }, new Map).values()];
    
    console.log(output);