I have an array of numbers sorted in ascending order. How to group the "closest" numbers (where the difference between the numbers is less than 5, for example)? So out of [1, 4, 5, 23, 40, 55, 56, 100, 234] I want to get [[1, 4, 5], 23, 40, [55, 56], 100, 234]
I have an array of numbers sorted in ascending order. How to group the "closest" numbers (where the difference between the numbers is less than 5, for example)? So out of [1, 4, 5, 23, 40, 55, 56, 100, 234] I want to get [[1, 4, 5], 23, 40, [55, 56], 100, 234]
Share Improve this question asked Jan 18, 2017 at 16:41 ZhurovichZhurovich 755 bronze badges 1- 1 you need to show some effort into solving the problem, paste your code so far if you have any – Brian Commented Jan 18, 2017 at 16:44
5 Answers
Reset to default 7You could check the predecessor and if in range add to array.
var data = [1, 4, 5, 23, 40, 55, 56, 100, 234],
result = data.reduce(function (r, a, i, aa) {
if (a - aa[i - 1] < 5) {
if (!Array.isArray(r[r.length - 1])) {
r[r.length - 1] = [r[r.length - 1]];
}
r[r.length - 1].push(a);
return r;
}
r.push(a);
return r;
}, []);
console.log(result); // [[1, 4, 5], 23, 40, [55, 56], 100, 234]
Use Array#reduce
method with an empty array as the initial value where update the array by checking the last element.
var data = [1, 4, 5, 23, 40, 55, 56, 100, 234];
var res = data.reduce(function(arr, e) {
// cache last element index
var i = arr.length - 1;
// check array exist atleast one element
if (i > -1) {
// check last element is array
if (Array.isArray(arr[i])) {
// pare last element in array and current element is adjacent
// if yes push into that array otherwise push into main array
e - arr[i][arr[i].length - 1] < 5 ? arr[i].push(e) : arr.push(e);
// if last element is not an array
} else {
// if last element and current element is adjacent
// update last element with an array or just push as usual
e - arr[i] < 5 ? arr[i] = [arr[i], e] : arr.push(e);
}
// if array is empty
} else
// simply push the element
arr.push(e);
// retuen the array reference
return arr;
// set initial value as an epty array
}, [])
console.log(res);
What about another (may shorter)reduce:
newarray=array.reduce((newarray,number)=>{
var last=newarray.pop()||[];
if(last.every(elem=>elem+6>number)){
last.push(number);
newarray.push(last);
return newarray;
}
newarray.push(last);
newarray.push([number]);
return newarray;
},[]);
You can use reduce and check last element added to add to last array or insert new one, and when you get to last element of data you use map
to remove arrays with one element.
var data = [1, 4, 5, 23, 40, 55, 56, 100, 234];
var result = data.reduce(function(r, e, i) {
if (i != 0) {
(e - data[i - 1] < 5) ? r[r.length - 1].push(e): r.push([e])
} else {
r.push([e])
}
if (i == data.length - 1) r = r.map(e => e.length == 1 ? e[0] : e)
return r;
}, [])
console.log(result)
This is a good question...
Though my answer is slightly convoluted, for experimental purposes you can also do like this...
var data = [1, 4, 5, 23, 40, 55, 56, 100, 234],
result = data.reduce((p,c) => p.length ? p[p.length-1].length ? c-p[p.length-1][p[p.length-1].length-1] < 5 ? (p[p.length-1].push(c),p)
: (p.push(c),p)
: c-p[p.length-1] < 5 ? (p[p.length-1] = [p[p.length-1],c],p)
: (p.push(c),p)
: c-p < 5 ? [[p].concat(c)]
: [p,c]);
console.log(JSON.stringify(result));
I think it's pretty efficient too.
Ok so let's try something funny... We will feed this algorithm with an array of 1,000,000 random items among 1 ~ 5,000,000 in sorted order and measure how long it takes to group them for a diff < 5.
var data = Array(1000000).fill()
.map(_ => ~~(Math.random()*5000000)+1)
.sort((a,b) => a-b),
result = [];
console.time("grouping");
result = data.reduce((p,c) => p.length ? p[p.length-1].length ? c-p[p.length-1][p[p.length-1].length-1] < 5 ? (p[p.length-1].push(c),p)
: (p.push(c),p)
: c-p[p.length-1] < 5 ? (p[p.length-1] = [p[p.length-1],c],p)
: (p.push(c),p)
: c-p < 5 ? [[p].concat(c)]
: [p,c]);
console.timeEnd("grouping");
console.log(result.length);
Like ~200ms in average. Seems cool..!