SubSul
SubSul

Reputation: 2563

Sort array of objects using three levels of custom sort criteria

I'm trying to sort an array of objects using three levels of custom sorting criteria.

firstLevelSortOrder = ['10K','20K','30K','40K','50K']
secondLevelSortOrder = ['ACTIVE','CONSUMED','EXPIRED']
thirdLevelSortOrder = 
1) Oldest bountyIssueDate in case of multiple ACTIVE objects
2) Latest bountyIssueDate in case of multiple CONSUMED objects
3) Latest bountyIssueDate in case multiple EXPIRED objects

Array to sort :

let bountyList = [
  {
    "bountyCode": "10K",
    "bountyIssueDate": "13 Jan 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "20K",
    "bountyIssueDate": "13 Feb 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "13 Mar 2020",
    "bountyStatus": "EXPIRED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "13 Apr 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "50K",
    "bountyIssueDate": "13 May 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "29 May 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "06 Jun 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "15 Mar 2020",
    "bountyStatus": "CONSUMED"
  },
    {
    "bountyCode": "40K",
    "bountyIssueDate": "23 Apr 2020",
    "bountyStatus": "CONSUMED"
  }
]

Result array:

[
  {
    "bountyCode": "10K",
    "bountyIssueDate": "29 May 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "06 Jun 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "13 Jan 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "20K",
    "bountyIssueDate": "13 Feb 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "15 Mar 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "13 Mar 2020",
    "bountyStatus": "EXPIRED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "23 Apr 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "03 Apr 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "50K",
    "bountyIssueDate": "13 May 2020",
    "bountyStatus": "CONSUMED"
  }
]

I've got the first level of sort working which is here in this fiddle. Hitting a dead end trying to integrate the second and third level sort order. Also tried separating out the duplicates into a separate array, sorting them and then merge them into a master array, its getting very messy.

Upvotes: 1

Views: 63

Answers (3)

BeeShopRocks
BeeShopRocks

Reputation: 124

The previous answers are great but I wanted some easy solution not requiring conditional statement. I just sort by level 1 and level 2 arrays without considering the date.

var bountyList = [{
    "bountyCode": "10K",
    "bountyIssueDate": "13 Jan 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "20K",
    "bountyIssueDate": "13 Feb 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "13 Mar 2020",
    "bountyStatus": "EXPIRED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "13 Apr 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "50K",
    "bountyIssueDate": "13 May 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "29 May 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "06 Jun 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "15 Mar 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "23 Apr 2020",
    "bountyStatus": "CONSUMED"
  }
]

var firstLevelSortOrder = ['10K', '20K', '30K', '40K', '50K']
var secondLevelSortOrder = ['ACTIVE', 'CONSUMED', 'EXPIRED']
const res = bountyList.sort( function(a, b) {
    let lv1 = firstLevelSortOrder.indexOf(a.bountyCode) - firstLevelSortOrder.indexOf(b.bountyCode)
    let lv2 = firstLevelSortOrder.indexOf(a.bountyStatus) - firstLevelSortOrder.indexOf(b.bountyStatus);
    return lv2 - lv1;
}).reverse();
console.log(res);

Upvotes: 1

Siva Kondapi Venkata
Siva Kondapi Venkata

Reputation: 11001

Sort compare function, check whether code same or not. if same code, then check for status if same status, then check for date.

let bountyList = [
  {
    bountyCode: "10K",
    bountyIssueDate: "13 Jan 2020",
    bountyStatus: "CONSUMED",
  },
  {
    bountyCode: "20K",
    bountyIssueDate: "13 Feb 2020",
    bountyStatus: "CONSUMED",
  },
  {
    bountyCode: "30K",
    bountyIssueDate: "13 Mar 2020",
    bountyStatus: "EXPIRED",
  },
  {
    bountyCode: "40K",
    bountyIssueDate: "13 Apr 2020",
    bountyStatus: "CONSUMED",
  },
  {
    bountyCode: "50K",
    bountyIssueDate: "13 May 2020",
    bountyStatus: "CONSUMED",
  },
  {
    bountyCode: "10K",
    bountyIssueDate: "29 May 2020",
    bountyStatus: "ACTIVE",
  },
  {
    bountyCode: "10K",
    bountyIssueDate: "06 Jun 2020",
    bountyStatus: "ACTIVE",
  },
  {
    bountyCode: "30K",
    bountyIssueDate: "15 Mar 2020",
    bountyStatus: "CONSUMED",
  },
  {
    bountyCode: "40K",
    bountyIssueDate: "23 Apr 2020",
    bountyStatus: "CONSUMED",
  },
];

const compare = (a, b) => {
  const av = parseInt(a.bountyCode);
  const bv = parseInt(b.bountyCode);
  const status = ["ACTIVE", "CONSUMED", "EXPIRED"];
  if (av === bv) {
    const as = status.indexOf(a.bountyStatus);
    const bs = status.indexOf(b.bountyStatus);
    if (as !== bs) {
      return as - bs;
    } else if (as === 0) {
      return a.bountyIssueDate > b.bountyIssueDate;
    } else {
      return a.bountyIssueDate < b.bountyIssueDate;
    }
  } else {
    return av - bv;
  }
};

bountyList.sort(compare);

console.log(bountyList);

Upvotes: 1

Nick
Nick

Reputation: 147206

You need to implement this based on checking each sort criteria in order. If the first level sort criteria gives a result, return that. Otherwise, check the second level. If that gives a result, return that. Otherwise, return the result of the third level criteria:

var bountyList = [{
    "bountyCode": "10K",
    "bountyIssueDate": "13 Jan 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "20K",
    "bountyIssueDate": "13 Feb 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "13 Mar 2020",
    "bountyStatus": "EXPIRED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "13 Apr 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "50K",
    "bountyIssueDate": "13 May 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "29 May 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "10K",
    "bountyIssueDate": "06 Jun 2020",
    "bountyStatus": "ACTIVE"
  },
  {
    "bountyCode": "30K",
    "bountyIssueDate": "15 Mar 2020",
    "bountyStatus": "CONSUMED"
  },
  {
    "bountyCode": "40K",
    "bountyIssueDate": "23 Apr 2020",
    "bountyStatus": "CONSUMED"
  }
]

var firstLevelSortOrder = ['10K', '20K', '30K', '40K', '50K']
var secondLevelSortOrder = ['ACTIVE', 'CONSUMED', 'EXPIRED']

console.log(bountyList.sort(
  function(a, b) {
    var first = firstLevelSortOrder.indexOf(a.bountyCode) - firstLevelSortOrder.indexOf(b.bountyCode);
    if (first != 0) {
      return first;
    }
    // first order equal, test second order
    var second = secondLevelSortOrder.indexOf(a.bountyStatus) - secondLevelSortOrder.indexOf(b.bountyStatus);
    if (second != 0) {
      return second;
    }
    // second order also equal, test dates dependent on bountyStatus
    if (a.bountyStatus == 'ACTIVE') {
      // want earliest date sorted first
      return Date.parse(a.bountyIssueDate) - Date.parse(b.bountyIssueDate);
    } 
    else {
      // want latest date sorted first for CONSUMED or EXPIRED
      return Date.parse(b.bountyIssueDate) - Date.parse(a.bountyIssueDate);
    }
  }
))

Note that for convenience to demonstrate the code I've used Date.parse() to parse the dates in your objects. In general this is not recommended due to browser inconsistencies (see the manual) and you should manually parse the dates into a timestamp for this purpose.

Upvotes: 5

Related Questions