Toucouleur
Toucouleur

Reputation: 1242

_.groupBy but _.sort first

I got this array,

I need to group on id, but first sort it by timestamp

sample_array = [
    {id: 21, timestamp:1, year: 2012},
    {id: 11, timestamp:2, year: 2017},
    {id: 12, timestamp:3, year: 2016},
    {id: 11, timestamp:4, year: 2014},
    {id: 11, timestamp:5, year: 2015},
    {id: 19, timestamp:6, year: 2016},
    {id: 12, timestamp:7, year: 2016}
    ]

What i try to achieve using underscore.js is to sort my array using timestamp first, but then check if exists another entry for same id, then group on it before next id:

expected results :

[
    {id: 21, timestamp:1, year: 2012},
    {id: 11, timestamp:2, year: 2017},
    {id: 11, timestamp:4, year: 2014},
    {id: 11, timestamp:5, year: 2015},
    {id: 12, timestamp:3, year: 2016},
    {id: 12, timestamp:7, year: 2016},
    {id: 19, timestamp:6, year: 2016}
]

I try to achieve this by using combination of _.groupBy and _.sortBy.

The need is to :

  1. group by id
  2. sort by timestamp in each group
  3. sort groups by their minimum timestamp (first timestamp in each group)

Upvotes: 1

Views: 410

Answers (3)

Nina Scholz
Nina Scholz

Reputation: 386550

You could sort by timestamp and then sort by id, but do not take the number, because numbers are sorted to top in objects. Therefore you need to convert the number to a string and group by it.

Then map the values and flatten the arrays.

var array = [{ id: 21, timestamp:1, year: 2012 }, { id: 11, timestamp:2, year: 2017 }, { id: 12, timestamp:3, year: 2016 }, { id: 11, timestamp:4, year: 2014 }, { id: 11, timestamp:5, year: 2015 }, { id: 13, timestamp:6, year: 2016 }, { id: 12, timestamp:7, year: 2016 }],
    result = _.chain(array)
        .sortBy(['timestamp'])
        .groupBy(o => _ + o.id)
        .map(v => v)
        .flatten()
        .value();

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Upvotes: 2

Naim Sulejmani
Naim Sulejmani

Reputation: 1120

If this array is the problem, you can solve simply as follows, you can play with digits if zeros adding to timestamp, an taking the exact number for each timestamp :), check it should work (this example is maximum 5 digits of timestamp) if it has more you can add zeros at beginings

    sample_array.sort(function(a,b){
return Number(a.id.toString()+('00000'+a.timestamp.toString()).substr(a.timestamp.toString().length-1))-Number(b.id.toString()+('00000'+b.timestamp.toString()).substr(b.timestamp.toString().length-1))
})

Upvotes: 0

skyboyer
skyboyer

Reputation: 23705

Based on next understanding:

group by id, sort by timestamp in each group then sort groups by their minimum timestamp(first timestamp in each group actually) and flatten groups into flat list.

_.chain(sample_array)
 .groupBy('id')
 .mapObject(groupPerId=> _.sortBy(groupPerId, 'timestamp'))
 .sortBy(groupPerId=> groupPerId[0].timestamp)
 .flatten()
 .value()

Upvotes: 1

Related Questions