skip to Main Content

For example, I have an array : [1,1,2,2,2,3,4,4,4], I want a result that group the same adjacent objects into another array : [[1,1],[2,2,2],[3],[4,4,4]], how can I do that? I tried:

const arr=[1,1,2,2,2,3,4,4,4];
const result=[];
for(let i=0,j=1;j<arr.length;j++){
  if(arr[j-1]!=arr[j]){
    result.push(arr.slice(i,j));
    i=j;
  }
}
document.write(JSON.stringify(result));

Which I expect [[1,1],[2,2,2],[3],[4,4,4,4]] but I don’t know why would it be [[1,1],[2,2,2],[3]] (missed the last group).

I also tried

const arr=[1,1,2,2,2,3,4,4,4];
const result=[];
for(let i=0,j=1;j<arr.length;j++){
  if(arr[j-1]!=arr[j] || j==arr.length-1){
    result.push(arr.slice(i,j));
      i=j;
  }
}
document.write(JSON.stringify(result));

but the result is [[1,1],[2,2,2],[3],[4,4]], which missed a "4" at the last group.

How do I fix it?

4

Answers


  1. You are not dealing with the last group. Your idea is obviously to push the last group (made of everything from i, included to j, excluded) when you find an element that is different from the previous.

    But the last group has no follower different from the previous.

    There are ways to solve that. For example

    const arr=[1,1,2,2,2,3,4,4,4];
    const result=[];
    for(let i=0,j=1;j<=arr.length;j++){
      if(j==arr.length || arr[j-1]!=arr[j]){
        result.push(arr.slice(i,j));
        i=j;
      }
    }
    document.write(JSON.stringify(result));

    Based on the fact that || is a lazy or. So if j is arr.length then arr[j-1]!=arr[j] would not be evaluated — which is fortunate, since that would be an index out of bound. Which, in javascript, is not a big deal: arr[j] would then just be undefined. Which is ok for us. Unless arr[j-1] also is, that is unless undefined is a possible value of arr content.

    You could also simply, after the loop, add every thing after the last i (but for that you need i to exist outside the loop).

    But I also want to point out another possibility, taken from "The Art of Computer Programming", and often overlook: a sentinel.

    Since your problem is the particular case of last group having no successor, just add a successor.

    const arr=[1,1,2,2,2,3,4,4,4];
    const result=[];
    arr.push('whateveraslongasitisnot4');
    for(let i=0,j=1;j<arr.length;j++){
      if(arr[j-1]!=arr[j]){
        result.push(arr.slice(i,j));
        i=j;
      }
    }
    document.write(JSON.stringify(result));

    Sure, that means altering arr. So it is not always feasible. But then it avoid the cost of testing j==arr.length at every iteration. Again, that is also avoidable by treating that special case after the group. So, I am not claiming sentinel is the only option, nor that it is the faster one. Just, that it worth to be known. As you can see, to the cost of just one line (adding the sentinel) I can keep your code as is.

    Note that the simpler solution (the one given in comments by Marc), that is just letting j go to arr.length included, is implicitly a version of that: arrays in javascript have implicit undefined after all their elements. So, if, as my whateveraslongasitisnot4, undefined is not to be found in array, and especially not at the end, then, that implicit undefined acts as a sentinel. Not sure it was thought that way, but that is a way of seeing it.

    Login or Signup to reply.
  2. You can use the below code to get the result that you are expecting.

    const a = [1, 1, 2, 2, 2, 3, 4, 4, 4, 1, 1]
    
    const result = [];
    let flag = true;
    let local = [];
    let last = null;;
    for (let i = 0; i < a.length; i++) {
      if (a[i] !== last) {
        if (local.length)
          result.push(local)
    
        last = a[i]
        local = []
      }
      local.push(a[i])
    }
    
    if (local.length) result.push(local)
    
    console.log(result)
    Login or Signup to reply.
  3. You can use reduce and Array.prototype.at to get the result.

    const arr=[1,1,2,2,2,3,4,4,4,];
    
    const result = arr.reduce((acc, curr) => {
        const lastArr = acc.at(-1);
        
        if(lastArr && lastArr[0] === curr) {
            lastArr.push(curr);
        } else {
            acc.push([curr]);
        }
        return acc;
    }, []);
    
    console.log(result);
    Login or Signup to reply.
  4. Maps can be useful in this case.

    const arr = [1,1,2,2,2,3,4,4,4];
    let map   = new Map();
    
    arr.forEach(v => map.get(v) ? map.get(v).push(v) : map.set(v, [v]));
    let result = [...map.values()];
    
    document.write(JSON.stringify(result));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search