I’ve written the following function:
const trends = hits.reduce((arr, curr, index, array) => {
if (arr.includes(curr)) return arr
if (curr + 1 === array[index + 1]) arr.push(curr, array[index + 1]);
return arr;
}, []);
The point is so that if an array contains a sequence of numbers which increase by 1 then this returns a new array with these values.
For instance: [1, 2, 3, 6, 10] would return [1, 2, 3].
The problem is: if there’s more than one sequence, I’d like to have it in a separate array (or in an array of subarrays). At this point, the function does the following [1, 2, 3, 6, 7, 8]. I also can’t predict how many trends there might be. How can I accomplish this?
Advertisement
Answer
A straightforward approach, based on two conditions whose precedence can not be changed/swapped, which actually also reads what it does …
function collectItemSequences(list, item, idx, arr) {
if ((item - 1) === arr[idx - 1]) {
// in case of a predecessor ...
// ... push item into the most recent sequence list.
list[list.length - 1].push(item);
} else if ((item + 1) === arr[idx + 1]) {
// else, in case of a successor ...
// ... create a new sequence list with its 1st item.
list.push([ item ]);
}
return list;
}
console.log(
[2, 4, 6, 8, 10, 12, 14].reduce(collectItemSequences, [])
);
console.log(
[2, 4, 5, 6, 8, 10, 11, 12, 14].reduce(collectItemSequences, [])
);
console.log(
[1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15].reduce(collectItemSequences, [])
);.as-console-wrapper { min-height: 100%!important; top: 0; }Based on the above approach one could implement a more generic one which allows the configuration of how to compute a current item’s sequence predecessor respectively sequence successor …
function collectItemSequencesByConditions(collector, item, idx, arr) {
const { getPredecessor, getSuccessor, list } = collector;
if (getPredecessor(item) === arr[idx - 1]) {
// push item into the most recent sequence list.
list[list.length - 1].push(item);
} else if (getSuccessor(item) === arr[idx + 1]) {
// create a new sequence list with its 1st item.
list.push([ item ]);
}
return collector;
}
const conditions = {
getPredecessor: currentItem => currentItem - 2,
getSuccessor: currentItem => currentItem + 2,
};
console.log(
[2, 4, 6, 8, 10, 12, 14].reduce(
collectItemSequencesByConditions,
{ ...conditions, list: [] },
).list
);
console.log(
[2, 4, 5, 6, 8, 10, 11, 12, 14].reduce(
collectItemSequencesByConditions,
{ ...conditions, list: [] },
).list
);
console.log(
[1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 15].reduce(
collectItemSequencesByConditions,
{ ...conditions, list: [] },
).list
);.as-console-wrapper { min-height: 100%!important; top: 0; }Edit
The OP’s Q
I set up two pair of conditions, one for item
- 1, item+ 1, second pair respectively for- 10,+ 10. The hits array was[22, 31, 32, 33, 42, 52]. I turned yourconsole.logsintoconst variable = hits.reduce...so on. Then I returned both variables. The results were[31, 32, 33]and[42, 52]. The expected outcome for second is of course[22, 33, 42, 52].
Firstly the OP most probably meant [22, 32, 42, 52].
Secondly …
Nope, math is reliable. And the algorithm can’t be tricked. The rules which are applied for valid predecessors/successors are merciless. Thus the “expected outcome for” [22, 31, 32, 33, 42, 52] and +/- 10 of cause is [42, 52] and not [22, 32, 42, 52].
Why?.. The second value of [22, 31, 32, 33, 42, 52] is 31 which breaks any possible sequence (the OP expected 22, 32). Thus it is not a valid predecessor/successor sequence.
Here are some test cases …
console.log(
"for [22, 31, 32, 33, 42, 52] and [-1 , +1]",
"nexpect: '[[31,32,33]]' ?",
JSON.stringify([22, 31, 32, 33, 42, 52].reduce(
collectItemSequencesByConditions, {
getPredecessor: currentItem => currentItem - 1,
getSuccessor: currentItem => currentItem + 1,
list: [],
}
).list) === '[[31,32,33]]'
);
console.log(
[22, 31, 32, 33, 42, 52].reduce(
collectItemSequencesByConditions, {
getPredecessor: currentItem => currentItem - 1,
getSuccessor: currentItem => currentItem + 1,
list: [],
}
).list
);
console.log(
"for [22, 31, 32, 33, 42, 52] and [-10 , +10]",
"nexpect: '[[42,52]]' ?",
JSON.stringify([22, 31, 32, 33, 42, 52].reduce(
collectItemSequencesByConditions, {
getPredecessor: currentItem => currentItem - 10,
getSuccessor: currentItem => currentItem + 10,
list: [],
}
).list) === '[[42,52]]'
);
console.log(
[22, 31, 32, 33, 42, 52].reduce(
collectItemSequencesByConditions, {
getPredecessor: currentItem => currentItem - 10,
getSuccessor: currentItem => currentItem + 10,
list: [],
}
).list
);
console.log(
"for [21, 22, 32, 33, 42, 52] and [-10 , +10]",
"nexpect: '[[22,32],[42,52]]' ?",
JSON.stringify([21, 22, 32, 33, 42, 52].reduce(
collectItemSequencesByConditions, {
getPredecessor: currentItem => currentItem - 10,
getSuccessor: currentItem => currentItem + 10,
list: [],
}
).list) === '[[22,32],[42,52]]'
);
console.log(
[21, 22, 32, 33, 42, 52].reduce(
collectItemSequencesByConditions, {
getPredecessor: currentItem => currentItem - 10,
getSuccessor: currentItem => currentItem + 10,
list: [],
}
).list
);.as-console-wrapper { min-height: 100%!important; top: 0; }<script>
function collectItemSequencesByConditions(collector, item, idx, arr) {
const { getPredecessor, getSuccessor, list } = collector;
if (getPredecessor(item) === arr[idx - 1]) {
// push item into the most recent sequence list.
list[list.length - 1].push(item);
} else if (getSuccessor(item) === arr[idx + 1]) {
// create a new sequence list with its 1st item.
list.push([ item ]);
}
return collector;
}
</script>