geekchic
geekchic

Reputation: 2431

Grouping consecutive elements together using Javascript

I have an array of elements like so:

messages[i], where messages[i] may only exist for certain values of i. For instance messages[0] and messages[2] may exist but not messages[1].

Now I would like to group together elements with continuous indices, for example if the indices for which messages existed were:

2, 3, 4, 5, 8, 9, 12, 13, 14, 15, 16, 17, 20

I would like to group them like so:

2, 3, 4, 5

8, 9

12, 13, 14, 15, 16, 17

20

What would be an effective way to do so using Javascript?

EDIT:

for (i = 0; i < messages.length; i++) {
   if (messages[i].from_user_id == current_user_id) {
   // group the continuous messages together
      } else {
  //group these continuous messages together
   }
}

Upvotes: 6

Views: 4225

Answers (5)

Ben Aston
Ben Aston

Reputation: 55739

const cluster = (arr, tmp = [], result = []) => 
    (result = arr.reduce((acc, c, i) => 
        (!tmp.length || c === (arr[i-1]+1)
            ? (tmp.push(c), acc) 
            : (acc.push(tmp), tmp = [c], acc))
            , []), tmp.length ? (result.push(tmp), result) : result)

console.log(cluster([2, 3, 4, 5, 8, 9, 12, 13, 14, 15, 16, 17, 20]))

Upvotes: 0

Marjo Forcado
Marjo Forcado

Reputation: 43

Another approach would be something like this. I'm using a library called lodash for my array manipulation.

Basically I'm sorting the array in ascending order. And then for every increment, I'm storing the current element to a temporary array and comparing the last value of that array to the current element if they are in sequence if not I push the values of the temporary array to my result array and so on. If my loop reaches the end I just push the values of my temporary array to my results array.

var _ = require('lodash');
var arr = [2, 3, 4, 5, 8, 9, 12, 13, 14, 15, 16, 17, 20];
arr = _.sortBy(arr, function (o) {
    return o;
});
var tmp = [];
var res = [];
for (var i = 0; i < arr.length; i++) {
    if (tmp.length === 0) {
        tmp.push(arr[i]);
    }
    else {
        var lastEl = _.last(tmp);
        if ((lastEl + 1) === arr[i]) {
            tmp.push(arr[i]);
        }
        else {
            res.push(tmp);
            tmp = [];
            tmp.push(arr[i]);
        }
        if (i === (arr.length - 1)) {
            res.push(tmp);
            tmp = [];
        }
    }
}
// Outputs: [ [ 2, 3, 4, 5 ], [ 8, 9 ], [ 12, 13, 14, 15, 16, 17 ], [ 20 ] ]

Upvotes: 0

Alnitak
Alnitak

Reputation: 339826

Given :

var data = [ undefined, undefined, 2, 3, 4, 5,
             undefined,undefined, 8, 9,
             undefined, undefined, 12, 13, 14, 15, 16, 17,
             undefined, undefined, 20];

(or the almost equivalent array where the undefined elements don't exist at all, but where the defined elements have the same indices as above) this reduce call will return a two-dimensional array where each top level element is the contents of the original array, grouped by contiguously defined entries:

var r = data.reduce(function(a, b, i, v) {
    if (b !== undefined) {              // ignore undefined entries
        if (v[i - 1] === undefined) {   // if this is the start of a new run
            a.push([]);                 // then create a new subarray
        }
        a[a.length - 1].push(b);        // append current value to subarray
    }
    return a;                           // return state for next iteration
}, []);                                 // initial top-level array

i.e. [[ 2, 3, 4, 5], [8, 9], [12, 13, 14, 15, 16, 17], [20]]

NB: this could also be written using a .forEach call, but I like .reduce because it requires no temporary variables - all state is encapsulated in the function parameters.

Upvotes: 4

thefourtheye
thefourtheye

Reputation: 239483

You can use a counter variable which has to be incremented and the difference between the index and the consecutive elements are the same, group them in a temporary array. If the difference is varies for two consecutive array elements, the temporary element has to be moved to the result and the temporary array has to be assigned a new array object.

var array = [2, 3, 4, 5, 8, 9, 12, 13, 14, 15, 16, 17, 20];

var result = [], temp = [], difference;
for (var i = 0; i < array.length; i += 1) {
    if (difference !== (array[i] - i)) {
        if (difference !== undefined) {
            result.push(temp);
            temp = [];
        }
        difference = array[i] - i;
    }
    temp.push(array[i]);
}

if (temp.length) {
    result.push(temp);
}

console.log(result);
# [ [ 2, 3, 4, 5 ], [ 8, 9 ], [ 12, 13, 14, 15, 16, 17 ], [ 20 ] ]

Upvotes: 4

Nathan Merrill
Nathan Merrill

Reputation: 8386

I would iterate through the list, and if you find an element at messages[i], add i to a list of mins. Then, once you don't find an element at messages[j], and j to a list of maxes.

Then you will have two lists (or one, if you use a container, as I probably would) that contains the start and stop indexes of the groups.

Upvotes: 0

Related Questions