Reputation: 1898
I have an array with numbers.
var myArray = [1, 2, 3, 5, 8, 9, 10];
and I'd like to group them into a string with a from~to
format until the next value is not continuous with the previous value, like the following example.
var outputArray = ["1~3", "5", "8~10"];
How to do that?
Upvotes: 3
Views: 677
Reputation: 1
For typescript, this function takes a sorted array of numbers and groups them into a single string.
function convertNumberArrayToRangeString(numbers: number[]): string {
const delimiter = '~';
return numbers
.reduce((accumulator: string, currentValue: number, index: number, array: number[]) => {
if (index > 0 && array[index - 1] + 1 !== currentValue) {
if (index > 2 && array[index - 1] - array[index - 2] === 1) {
accumulator += delimiter + array[index - 1].toString();
}
accumulator += ',' + currentValue.toString();
} else if (index === 0 || index === array.length - 1) { // first or last
if (array[index - 1] === currentValue - 1) {
accumulator += delimiter;
} else if (index > 0) {
accumulator += ',';
}
accumulator += currentValue.toString();
}
return accumulator;
}, '');
}
const list = [1, 2, 3, 6, 7, 8]; // sorted unique list
const groupedList = convertNumberArrayToRangeString(list);
console.log(groupedList);
Output> "1~3,6~8"
Upvotes: 0
Reputation: 1898
Click here for CodePen (or a Chinese(中文)version).
Basically just create a lastValue
variable, and iterating the array. If the currentValue - 1
equals the lastValue
then it's a continuous number. Create a new group once the condition failed.
In the final you just merge the groups with a string.
var newArray = [];
var tempArray = [];
var oldArray = [1, 2, 3, 5, 8, 9, 10];
var lastValue = null;
oldArray.forEach(value => {
if (lastValue != value - 1 || lastValue === null) {
tempArray.push([value, value]);
} else {
tempArray[tempArray.length - 1][1] = value;
}
lastValue = value;
});
tempArray.forEach(value => {
if (value[0] === value[1]) {
newArray.push(value[0].toString());
} else {
newArray.push(value.join("~"));
}
});
// OUTPUS: (3) ["1~3", "5", "8~10"]
document.write(JSON.stringify(newArray));
Upvotes: 0
Reputation: 12209
You can do this with Array.reduce()
followed by Array.map()
:
const nums = [1, 2, 3, 5, 8, 9, 10]
const res = nums.reduce((acc, cur, idx, arr) => {
if (idx === 0 || cur - 1 > arr[idx - 1]) {
acc.push([cur])
} else {
acc[acc.length - 1].push(cur)
}
return acc
}, []).map(cur => cur.length > 1 ? cur.shift() + "~" + cur.pop() : cur[0])
console.log(res)
Upvotes: 2
Reputation: 11001
Go thru only one time all elements. Whenever there is pattern of numbers not in sequence, just put them in results array.
var myArray = [1, 2, 3, 5, 8, 9, 10];
let start = myArray[0];
let end = myArray[0];
const results = [];
for (let i = 1; i < myArray.length; i++) {
if (myArray[i] === (end + 1)) {
end = myArray[i];
} else {
results.push((start === end) ? `${start}` : `${start}~${end}`);
start = myArray[i];
end = myArray[i];
}
}
results.push((start === end) ? `${start}` : `${start}~${end}`);
console.log(results);
Upvotes: 1
Reputation: 386654
You could take a slightly shorter approach by using the index to check if the first element needs an new array or if the value is not in order.
For the second mapping, you could just join all elements.
function group(numbers) {
return numbers
.reduce((result, value, index, array) => {
if (!index || array[index - 1] + 1 !== value) {
result.push([value]);
} else {
result[result.length - 1][1] = value;
}
return result;
}, [])
.map(array => array.join('~'));
}
console.log(group([1, 2, 3, 5, 8, 9, 10]));
Upvotes: 4