I have an array of strings, which are either an asterisk by itself ("*"
) or something else.
I want to "merge" consecutive asterisks as follows:
If there are one or more asterisks in a row, remove them from the array, and prepend a single asterisk to the next non-asterisk element. If the asterisks are at the end of the array, simply remove them.
Some smaller examples:
[..., "*", "4", ...] => [..., "*4", ...] // concat asterisk with string at the right
[..., "*", "*", "4", ...] => [..., "*4", ...] // remove first asterisk
[..., "*", "4", "*"] => [..., "*4"] // remove last asterisk
[..., "4", "*", "*", "*"] => [..., "4" ] // remove all asterisk which have no alphanumeric at its right
For this full-sized input:
let data = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];
The result should be:
["0", "1", "2", "3", "4", "5", "6", "*9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*34", "*36", "37", "*39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*54", "*56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*91", "92", "93", "94", "95", "*97"]
How can I implement this? Here is my attempt:
let data = ["0", "1", "2", "3", "4", "5", "6", "*", "*", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "*", "34", "*", "36", "37", "*", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "*", "54", "*", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "*", "*", "*", "*", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "*", "91", "92", "93", "94", "95", "*", "97", "*", "*"];
let output = data.map((d, index, arr) => arr[index - 1] === '*' ? '*'+d : d).filter(d => d !== '*');
console.log(output);
7
Answers
You are almost there! The one missing feature is testing for successive
*
s?Here is a solution.
Explanation-
*<non_star>
then merge both and push them into the new array.*<curent_item>
then push it too.As for the OP’s use case I propose an array
join
, regex-based stringreplace
and stringsplit
approach where the regular expressions patterns is as follows …… and its description is provided with the pattern’s test/playground page.
The advantage comes with the expressiveness of dealing with the provided array data as one big string (hence
join
ing the array) and processing it with a single regex-basedreplace
task, where the intermediate replacement result issplit
again into the final array structure.Edit … in order to provide a "more to the ground" approach.
Final Note I myself find the regex based variant still more elegant and expressive though the later provided above solution might be easier to read/maintain for beginners.
You can do this in a single loop by simply checking if the current element is an
*
, if not thenpush()
, concatenating if the previous element was an*
.Or the same logic in a
reduce()
call if you preferAlternatively you can avoid the second condition (and thus the second array access) in the concatenation by storing the previous value on each iteration. This is similar to the state machine answer in function, but avoids the repeated destructuring and object creation.
Basic online benchmark
This can be modeled as a simple state machine:
We start with an empty state value. When the state is empty and we encounter an asterisk, we move to the asterisk state. When we encounter a different value, we append it to our output and stay in the empty state. When we’re in the asterisk state, if we encounter an asterisk, we stay in the asterisk state and leave the output alone. When we encounter something else, we append
*<value>
to our output, and return to the empty state. There’s no cleanup needed at the end; we just return the output.We can code that state machine with a simple
reduce
call, which tracks astate
variable and the currentoutput
array, like this:Because our state value is either an asterisk or an empty string, we can just prepend it to our value and append the result to the output array.
Here is an optimized version of pilchard’s "Simple loop (store prev)" solution:
The differences are:
prev
instead of a stringprev
only when necessaryThe benchmark gives: