Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort functionally an array based on attribute and group bits of the array together

I have this array:

const nums = [
 {name: 'a', option: false},
 {name: 'b', option: false},
 {name: 'c', option: false},
 {name: 'd', option: false},
 {name: 'e', option: false},
 {name: 'f', option: true},
 {name: 'g', option: true},
 {name: 'h', option: false},
 {name: 'i', option: true},
 {name: 'j', option: false},
 {name: 'k', option: false},
];

I would like to reorganize this array so that elements so that when moving through the array, elements that have the "false" option are grouped together by groups of 4 elements, but if there is an element that has the "true" option, it takes precedence and is put before the groups of 4 elements, except if the preceding group of 4 elements preceding is complete (ie its length is equal to 4).

The result would be

const nums = [
 {name: 'a', option: false},
 {name: 'b', option: false},
 {name: 'c', option: false},
 {name: 'd', option: false},
 {name: 'f', option: true},
 {name: 'g', option: true},
 {name: 'i', option: true},
 {name: 'e', option: false},
 {name: 'h', option: false},
 {name: 'j', option: false},
 {name: 'k', option: false},
];

I have succeeded in a imperative style

let stock = [];
let array2 = [];
for (let i = 0; i < nums.length; i ++){
  if (nums[i].option){
    array2.push(nums[i]);
  }
  else {
    stock.push(nums[i]);
    if(stock.length == 4) {
      array2 = array2.concat(stock);
      stock = [];
    }
  }
}

console.log('array2 is', array2);

So far, so good I would like now to do it in a functional manner This is my way:

let stock = [];
const actual = nums.map(
 (acc => val => {
   if (val.option){
     acc.push(val);
   }
   else {
     stock.push(val);
     if (stock.length === 4) {
       acc = acc.concat(stock);
       stock = [];
     }
   }
   return acc;
 })([]))

console.log('actual is, ', actual[actual.length-1]);

While it works, I think that the functional programming style could be improved. Could you help me improve my code ?

Note: Some people have found the question a bit unusual (which it is indeed). So I will provide some more context:

the underlying problematic is a UI style problem that cannot be solved with conventional style classes: I have a grid container that contains lines of four grid elements (when they are not "opened").

When it is opened, then the card takes all the line and the following elements are push back. The problem is that it is not pretty, because I have some lines that may contain only one, or two non opened elements, and nothing after until the next line, the opened card that takes all the space of the next line.

So the idea is to take groups of four non opened elements, stock them until I have a group of four, and flush them on one line. If I meet an opened card, I display this card first, and flush the non opened elements after.

like image 775
Makoto Avatar asked Apr 24 '26 23:04

Makoto


1 Answers

I'd actually go for an approach that is similar to yours, using a chunk as a temporary value to store incomplete groups.

const group = (data = [], chunk = [], res = []) => {
  if (!data.length) {
    // input is empty,
    // flush any possible incomplete chunk
    // and return result
    return [...res, ...chunk];
  }

  const [head, ...tail] = data;
  const next = (...xs) => group(tail, ...xs);
  
  if (head.option) {
    // head.option is true, so you can 
    // safely put it in tail position
    return next(chunk, [...res, head]);
  }
  
  if (chunk.length < 4) {
    // chunk is not-complete, so keep accumulating
    return next([...chunk, head], res);
  }

  // chunk is complete, append it to the rest 
  // and create a new one with the current value
  return next([head], [...res, ...chunk]);
};

const data = [
 {name: 'a', option: false},
 {name: 'b', option: false},
 {name: 'c', option: false},
 {name: 'd', option: false},
 {name: 'e', option: false},
 {name: 'f', option: true},
 {name: 'g', option: true},
 {name: 'h', option: false},
 {name: 'i', option: true},
 {name: 'j', option: false},
 {name: 'k', option: false},
];

console.log('result is', group(data));

Recursion is desirable here and their states are:

  1. empty input (base case)
  2. chunk, (res + head)
  3. (chunk + head), res
  4. head, (res + chunk)
like image 191
Hitmands Avatar answered Apr 27 '26 12:04

Hitmands



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!