fallais
fallais

Reputation: 597

Shuffle some elements of an array with a specific step

I need to shuffle some elements of an array with a specific step.

I have this array of object :

[
  {
    "_id": "aze54aze",
    "boosted": false
  },
  {
    "_id": "94v2d9e3",
    "boosted": false
  },
  {
    "_id": "999f5a8az4",
    "boosted": false
  },
  {
    "_id": "e9d29a9g",
    "boosted": true
  },
  {
    "_id": "f8f2a9f6a",
    "boosted": false
  },
  {
    "_id": "pe55z8a",
    "boosted": false
  },
  {
    "_id": "pvazpd97",
    "boosted": false
  },
  {
    "_id": "ft8azefze",
    "boosted": true
  },
]

I want the boosted objects to be shuffled within the array with a step of two objects between them. In order to not put them all at the beginning of the array, I need them to be "hidden" in the array.

That means that the first boosted object will go at the first position, then two normal objects, then the second boosted object, then the other normal objects.

Can I achieve this with Lodash built-in functions ?

Upvotes: 2

Views: 63

Answers (2)

Mihai Alexandru-Ionut
Mihai Alexandru-Ionut

Reputation: 48407

You can do this by using native ES6 javascript features like filter, reduce methods and spread syntax

First of all, you should separate the boosted and normal array items. Then create a new array where first boosted object will go at the first position, then two normal objects, then second boosted object and so on...

I used splice method in order to remove items from normal or boosted array after I added them to my result array. Why this ? Because at the end I need to concat the remaining elements to my result.

var arr = [ { "_id": "aze54aze", "boosted": false }, { "_id": "94v2d9e3", "boosted": false }, { "_id": "999f5a8az4", "boosted": false }, { "_id": "e9d29a9g", "boosted": true }, { "_id": "f8f2a9f6a", "boosted": false }, { "_id": "pe55z8a", "boosted": false }, { "_id": "pvazpd97", "boosted": false }, { "_id": "ft8azefze", "boosted": true }, ] 

var boosted = arr.filter(item => item.boosted);
var boosted_copy = boosted.slice();
var normal = arr.filter(item => !item.boosted);
var result = boosted.reduce((res, item, i) => {
   res.push(boosted_copy.splice(0, 1)[0], ...normal.splice(0, i * 2 + 2));
   return res;
}, []).concat(boosted_copy, normal);
console.log(result);

Upvotes: 2

Akrion
Akrion

Reputation: 18525

With ES6 you could achieve that by using reduce and spread syntax:

const data = [ { "_id": "aze54aze", "boosted": false }, { "_id": "94v2d9e3", "boosted": false }, { "_id": "999f5a8az4", "boosted": false }, { "_id": "e9d29a9g", "boosted": true }, { "_id": "f8f2a9f6a", "boosted": false }, { "_id": "pe55z8a", "boosted": false }, { "_id": "pvazpd97", "boosted": false }, { "_id": "ft8azefze", "boosted": true }, ]

const shuffleMeSoftly = (d, size) => {
  let g = d.reduce((r,c) => (r[c.boosted] = [...r[c.boosted] || [], c],r), {})
  return g.false.reduce((r,c,i) => {
     let nt = g.true[Math.floor(i/size)]
     r = i%size == 0 ? [...r || [], ...(nt ? [nt] : []), c] : [...r || [], c]
     return r
  }, [])
}

console.log(shuffleMeSoftly(data, 2))  // true every 2 false
console.log(shuffleMeSoftly(data, 3))  // true every 3 false

With lodash and ES6 spread syntax you could:

const data = [ { "_id": "aze54aze", "boosted": false }, { "_id": "94v2d9e3", "boosted": false }, { "_id": "999f5a8az4", "boosted": false }, { "_id": "e9d29a9g", "boosted": true }, { "_id": "f8f2a9f6a", "boosted": false }, { "_id": "pe55z8a", "boosted": false }, { "_id": "pvazpd97", "boosted": false }, { "_id": "ft8azefze", "boosted": true }, ]

const shuffleMeSoftly = (d, size) => {
   let grouped = _.groupBy(d, 'boosted')
   let chunks = _.chunk(grouped.false, size)
   return _.reduce(chunks, (r,c,i) => [...r, ...(grouped.true[i] ? [grouped.true[i]] : []), ...c], [])
}

console.log(shuffleMeSoftly(data, 2))   // true every 2 false
console.log(shuffleMeSoftly(data, 3))   // true every 3 false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

By using groupBy/chunk and then reduce.

It really is 2 lines if you get the chunks into the reduce instead of being its own variable but I left it for readability etc.

Upvotes: 1

Related Questions