Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort and Group an Immutable.js List

Given the following data structure:

var MyData = [
  {"id": 1, "status": "live", dateCreated: "12:00:00 01/02/2016"}, 
  {"id": 2, "status": "draft", dateCreated: "13:00:00 03/12/2015"}, 
  {"id": 3, "status": "ready", dateCreated: "16:00:00 04/09/2016"}, 
  {"id": 4, "status": "ready", dateCreated: "10:00:00 01/10/2016"}, 
  {"id": 5, "status": "live", dateCreated: "09:00:00 05/07/2015"}, 
  {"id": 6, "status": "draft", dateCreated: "08:00:00 11/03/2016"}, 
  {"id": 7, "status": "ready", dateCreated: "20:00:00 12/02/2016"}
]

I'm trying to sort and group it into these conditions:

  1. Grouped by status
  2. Ordered by status such that the order is "live", "draft", "ready"
  3. Items within each status should be ordered by dateCreated, most recent first.

What I have so far:

// this object will help us define our custom order as it's not alphabetical
const itemOrder = {
  'live': 1, 
  'ready': 2,
  'draft': 3
};

const sortByStatus = (statusA, statusB) => {
  if ( itemOrder[statusA] > itemOrder[statusB] ) return 1;
  if ( itemOrder[statusA] < itemOrder[statusB] ) return -1;
  return 0;
};

return List(MyData)
  .groupBy(item => item.status)
  .sort( sortByStatus )

Ignore for a moment the fact that I've not got to the point where I can sort by date yet :)

The problem with the above seems to be that sortByStatus is being passed the IndexedIterable that is the overall group but not it's key so I can't sort it by that key. I think I probably need to use sortBy but the Immutable.js docs are incomprehensible and have no examples on which to work out how to achieve this.

So, the question: how can I take the result of the groupBy action and sort it into a custom order and additionally how can I ensure that all the items in each group are sorted by date?

like image 841
slashwhatever Avatar asked Sep 27 '16 09:09

slashwhatever


People also ask

Is sort immutable JS?

Both, sort and reverse , are mutable in nature.

Are lists immutable in JavaScript?

js provides many Persistent Immutable data structures including: List , Stack , Map , OrderedMap , Set , OrderedSet and Record .

What does state Getin do?

it creates a Map, with only key a , which's value is plain object {b: {c: "banana"}} , and calling a map.

What is TOJS?

tojs lets you convert files between plain text, javascript strings, and document. write() statements. It's original purpose, was to read in an . html file, and output it as a series of document.


2 Answers

One easy fix is to just reach in to the first item of the array and get the status from there:

var MyData = [
  {"id": 1, "status": "live", dateCreated: "12:00:00 01/02/2016"}, 
  {"id": 2, "status": "draft", dateCreated: "13:00:00 03/12/2015"}, 
  {"id": 3, "status": "ready", dateCreated: "16:00:00 04/09/2016"}, 
  {"id": 4, "status": "ready", dateCreated: "10:00:00 01/10/2016"}, 
  {"id": 5, "status": "live", dateCreated: "09:00:00 05/07/2015"}, 
  {"id": 6, "status": "draft", dateCreated: "08:00:00 11/03/2016"}, 
  {"id": 7, "status": "ready", dateCreated: "20:00:00 12/02/2016"}
]

const itemOrder = {
  'live': 1, 
  'ready': 2,
  'draft': 3
};

const sortByStatus = (statusA, statusB) => {
var a = itemOrder[statusA.get(0).status];
var b = itemOrder[statusB.get(0).status];
console.log(statusA.get(0).status, a, statusB.get(0).status, b)
  if ( a > b ) return 1;
  if (a < b ) return -1;
  return 0;
};

var result = Immutable.List(MyData)
  .groupBy(item => item.status)
  .sort( sortByStatus );
  
  console.log(result.toJS())
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
like image 103
StefanHayden Avatar answered Oct 21 '22 23:10

StefanHayden


I think you were almost there. The important thing to notice is that .groupBy is returning a collection of lists, so when you call sort on that, you need a comparator that compares two different lists rather than two items. You can do this easily by just comparing the status of the first item in each list. After that, you want to sort each list individually by date, so you use .map to apply a change to each list in your list, then .sortBy on that list to sort by a specific key. Assuming you are using the built in Date type, just sorting by that field should do what you want.

var MyData = [
  {"id": 1, "status": "live", dateCreated: "12:00:00 01/02/2016"}, 
  {"id": 2, "status": "draft", dateCreated: "13:00:00 03/12/2015"}, 
  {"id": 3, "status": "ready", dateCreated: "16:00:00 04/09/2016"}, 
  {"id": 4, "status": "ready", dateCreated: "10:00:00 01/10/2016"}, 
  {"id": 5, "status": "live", dateCreated: "09:00:00 05/07/2015"}, 
  {"id": 6, "status": "draft", dateCreated: "08:00:00 11/03/2016"}, 
  {"id": 7, "status": "ready", dateCreated: "20:00:00 12/02/2016"}
]


Immutable.fromJs(MyData)
  // [{id: 1, ...}, ...]
  .groupBy(item => item.status) // group items into lists by status
  // [ 'live': [ { id: 1, ... }, { id:5, ... } ],
  //   'draft': [ { id: 2, ... }, { id: 6, ...} ],
  //   'ready': [ { id: 3, ... }, { id: 4, ... }, { id: 7, ... } ] ]
  .sort((listA, listB) => // order these groups by status
    itemOrder[listA.first().status] - itemOrder[listB.first().status])
  // [ 'live': [ { id: 1, ...}, ... ],
  //   'ready': [ { id: 3, ...}, ... ], 
  //   'draft': [ { id: 2, ...}, ... ] ]
  .map(list => list.sortBy(item => item.dateCreated)); // sort the elements in each group by date
  // [ 'live': [ { id: 5, ... }, { id: 1, ... } ],
  //   'ready': [ { id: 4, ... }, { id: 3, ... }, { id: 7, ... } ], 
  //   'draft': [ { id: 2, , ...}, { id: 6, ... } ] ]
like image 23
Simon Baumgardt-Wellander Avatar answered Oct 22 '22 00:10

Simon Baumgardt-Wellander