Compress array to group consecutive elements

Viewed 780

Following code compress one array so I can see how many times a value was seen in the array:

var str = "shopping-shopping-coupons-shopping-end";
var arr = str.split("-");

function compressArray(original) {
 
 var compressed = [];
 // make a copy of the input array
 var copy = original.slice(0);
 
 // first loop goes over every element
 for (var i = 0; i < original.length; i++) {
 
  var myCount = 0; 
  // loop over every element in the copy and see if it's the same
  for (var w = 0; w < copy.length; w++) {
   if (original[i] == copy[w]) {
    // increase amount of times duplicate is found
    myCount++;
    // sets item to undefined
    delete copy[w];
   }
  }
 
  if (myCount > 0) {
   var a = new Object();
   a.value = original[i];
   a.count = myCount;
   compressed.push(a);
  }
 }
 
 return compressed;
};

console.log(compressArray(arr));

However, I need to group elements only if they are repeated consecutively. Therefore, my desired output should be:

[{"value": "shopping", "count": 2},
 {"value": "coupons", "count": 1},
 {"value": "shopping", "count": 1},
 {"value": "end", "count": 1}]

Where should I modify the function so I prevent elements to be counted in a key if they are not consecutive?

2 Answers

You could reduce the values and check if the last value is equal to the actual one, then increment last count or add a new object.

function compressArray(original) {
    return original.reduce((r, value) => {
        var last = r[r.length - 1];
        if (last && value === last.value) {
            last.count++;
        } else {
            r.push({ value, count: 1 });
        }
        return r;
    }, []);
};

var str = "shopping-shopping-coupons-shopping-end",
    arr = str.split("-");

console.log(compressArray(arr));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Here's a functional implementation that solves your problem:

const compressArray = array => array.reduce((acc, item) => {
    const lastItem = acc[acc.length - 1]
    return lastItem && item === lastItem.value
        ? [...acc.slice(0, -1), { ...lastItem, count: lastItem.count + 1 }]
        : acc.concat({ value: item, count: 1 })
}, [])
Related