How to slice the array elements between the stars

Viewed 214

I need help for y problem, assume the following array:

let arr = [1,2,3,"*" , 4, "*" , 7 , 8 ,9 ,"*", "10","11", "*", "12" , "*"];

I want to have an output like this:
first array [1,2,3], second array [4], third array [7,8,9] and so on.

I can find all the * with filter but after that I can slice just with indexOf and lastIndexOf to get the first and the last *.indexOf(filteredElement,2) I can't perform a search for * after specific number, because the user input of the * can be different.

Any suggestions?

Thanks in advance guys

8 Answers

You can do it like this with reduce.

Use a temp variable keep on pushing value into it until you don't find a *, as soon as you find a * push this temp variable into output array and reset temp variable.

let arr = [1,2,3,"*" , 4, "*" , 7 , 8 ,9 ,"*", 10,11, "*", 12 , "*"];
let temp = []
let op = arr.reduce((o,c)=>{
  if(c !== '*'){
    temp.push(c)
  } else {
   if(temp.length){
    o.push(temp);
}
    temp=[];
  }
  return o;
},[])
console.log(op)

You could use slice method in combination with while loop statement.

function split_array(arr){
  let finalArr = [];
  i = 0;
  while(i < arr.length){
    j = i;
    
    while(arr[j] != "*"){ //find the sequence's end position.
      j++;
    }
    
    if(i!=j) //treat the case when first array item is *
      finalArr.push(arr.slice(i,j));
    
    while(arr[j] == "*"){ //skip consecutive * characters
      j++;
    }
    
    i = j;
  }
  return finalArr;
}
console.log(split_array([1,2,3,"*" , 4, "*" , 7 , 8 ,9 ,"*", 10,11, "*", 12 , "*"]));
console.log(split_array(["*",1,2,"*",7,8,9,"*","*",12,"*"]));

Hope this helps,

arr
    .join('|')
    .split('*')
    .filter((d) => d)
    .map((d) => d.split('|')
    .filter((d) => d));

A different solution could be to treat the array as a string and match with a regex.

So you match everything but the stars, creating the groupings, and then create you final array with the numbers.

const arr = [1, 2, 3, "*", 4, "*", 7, 8, 9, "*", 10, 11, "*", 12, "*"];

const res = arr.toString()
                .match(/[^*]+/g)
                .map(v => v.split(',')
                           .filter(v => v)
                           .map(v => +v));

console.log(res);

Another possibility with forEach and a bit of filtering:

const splitOnAsterisk = (arr) => {
  /* create an array to hold results with an initial empty child array */
  let result = [[]];
  /* create a new empty array in the result if current element is an asterisk,
     otherwise push to the last array in result… */
  arr.forEach(v =>
    v === "*"
    ? result.push([])
    : result[result.length - 1].push(v)
  );
  /* filter out empty arrays (if the first/last element was an asterisk
     or if there were two or more consecutive asterisks)
     [1, 2, "*", 3, "*"]
     ["*", 1, "*", 2, "*"]
     [1, 2, "*", "*", 3] etc…
  */
  return result.filter(a => a.length > 0);
}

console.log(splitOnAsterisk([1,2,3,"*",4,"*",7,8,9,"*",10,11,"*",12,"*"]))
console.log(splitOnAsterisk(["*",1,2,"*",7,8,9,"*","*",12,"*"]))
console.log(splitOnAsterisk(["*",1,"*","*",7,8,9,"*","*","*"]))

This can of course be generalised if you need so:

const splitArray = (arr, separator) => {
  let result = [[]];
  arr.forEach(v =>
    v === separator
    ? result.push([])
    : result[result.length - 1].push(v)
  );
  return result.filter(a => a.length > 0);
}

console.log(splitArray(["❤", "", "", "", "", ""], ""))

Magic (explanation in snippet)

((r=[],i=0)=>(arr.map(x=>x=="*"?i++:(r[i]=r[i]||[]).push(x)),r))();

let arr = [1,2,3,"*" , 4, "*" , 7 , 8 ,9 ,"*", "10","11", "*", "12" , "*"];

let out = ((r=[],i=0)=>(   arr.map(x=> x=="*" ? i++ : (r[i]=r[i]||[]).push(x))   ,r))();

console.log(JSON.stringify(out));

// Explanation - we use arrow function to init two variables:
// r=[] and i=0
// then we use arr.map to iterate and check x=="*" if no
// then we put value to r[i], if yes then we increase i and ommit value.

Here is a generalised function to partition an array. Similar to filter, it uses a callback, making it quite versatile.

const partitionArray = (arr, separatorTest) => {
  const output = [];
  let curr = []; // keep track of the current partition

  arr.forEach(el => {
    if (separatorTest(el)) { // if we hit a partition split point
      output.push(curr); // push the partition to the output
      curr = []; // and set the current partition to an empty array for the next partition
    }
    else {
      curr.push(el); // add the current element to the partition
    }
  });

  return output;
}

// usage:
const arr = [1,2,3,'*',4,'*',7,8,9,'*',10,11,'*',12,'*'];
const splitArr = partitionArray(arr, el => el == '*');

Ok, i'm going with another solution to this problem using the array splice() method. The idea here is to use a while loop and on each loop get an array of the elements that are before the next token separator. The splice() method using on this way will remove elements from the original array, so we can use the length of the array as an stop condition. Note also, that the number of loops required to finish is equal to the number of tokens separators you have into the original array.

let arr = [1, 2, 3, "*", 4, "*", 7, 8, 9, "*", "10", "11", "*", "12", "*"];
let result = [];
let token = "*";
let loopCounter = 0;

while (arr.length > 0)
{
    // Remove all elements from original array until find first token.
    // Push the removed array of elements inside the result.

    result.push(arr.splice(0, arr.indexOf(token)));

    // Remove a token separator from the array.

    arr.shift();
    loopCounter++;
}

console.log(JSON.stringify(result));
console.log(loopCounter);

Related