1

Let's say I have this array of strings (they're HTML elements, but we can use strings to keep it simple):

["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"]

I need a quick way to split this array up by "d". Kinda like String.split(), except for arrays. The end result should be something like this:

[["something", "else", "and"], ["more", "things", "in", "the"], ["array", "etc"]]

Are there any simple one-liners for this? Maybe there's a function built into JS and I'm just missing it?

0

9 Answers 9

2

Well if it's a one-liner you want, here you go:

var myArray = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"];

const result = myArray.reduce((a, c) => c === "d" ? (a.arr[++a.i] = []) && a : a.arr[a.i].push(c) && a, {arr: [[]], i: 0}).arr;

console.log(result);

Sign up to request clarification or add additional context in comments.

2 Comments

Yep that's a one-liner ;) heh. I am guessing OP was looking for a native method (different version of split) or method for binning for arrays, given a delimiter.
@vol7ron Ye I think so as well. Though unfortunetely there isn't, so something like this will have to do if you want a one-liner haha
2

One option would be to join by spaces, then split by ' d ', then split each subarray by spaces:

const input = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"];
const output = input
  .join(' ')
  .split(' d ')
  .map(str => str.split(' '));
console.log(output);

Or, without joining, figure out the index of every d, and slice every section of the input around the ds:

const input = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"];
const dIndicies = input.reduce((a, item, i) => {
  if (item === 'd') a.push(i);
  return a;
}, []);
const output = dIndicies.reduce((a, dIndex, i, arr) => {
  const nextDIndex = arr[i + 1];
  a.push(input.slice(dIndex + 1, nextDIndex));
  return a;
}, [input.slice(0, dIndicies[0] - 1)]);
console.log(output);

1 Comment

Nice solution. I hope OP doesn't intend for there to be spaces in any of the strings... :)
1

let myArray = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"];
let splitArray = [],
    tempArray = [];
myArray.forEach((ele, index) => {
    if(ele !== 'd') {
      tempArray.push(ele);
    }
    if(ele === 'd' || index === myArray.length - 1) {
      splitArray.push(tempArray);
      tempArray = []; 
    }
})

console.log(': ', splitArray);

Comments

1

A simple forEach approach is enough.

var arr = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"];
var result = [], temp = [];

arr.forEach(function(elem, index){
    elem !=='d' ? temp.push(elem) : (result.push(temp), temp = []);
    index==arr.length-1 && (result.push(temp));
});

console.log(result)

Comments

1

To answer your question, there aren't any concise one-liners that come to mind, but you may accomplish what you want with just a few lines of code by iterating over your values and if the word isn't 'd' store it; if it is, then create a new array to hold the next non-'d' value:

const words = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"]

let grouped = words.reduce((response,word)=>{
  if (word!=='d')
    response[response.length-1].push(word)
  else
    response[response.length]=[]
  return response
},[[]])

console.log(grouped)

1 Comment

Gave a one-liner solution for this. It's unreadable like crazy, but it works, lol
1

Use reduce starting with an accumulator that has an array containing an empty array. If the current item is the split value add an extra empty array to the end, otherwise spread the last array with the current item.

const arr = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"];

const splitArray = (array, val) =>
  array && array.length
    ? array.reduce(
        (results, item) =>
          item === val
            ? [...results, []]
            : [...results.filter((_, i) => i < results.length - 1), [...results[results.length - 1], item]],
        [[]]
      )
    : array;
  
  
console.log(splitArray(arr, 'd'));

2 Comments

It a little funky if the array starts or end in "d".
Not really, it works the same as string split as ',abc'.split(',') will give ['', 'abc'] so I am happy with it giving an empty array if the array starts with the split value.
1

You can make a pretty elegant recursive function with something like:

let arr = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"]

const spliton = (v, arr, i = arr.indexOf(v)) => (i < 0) 
    ? [arr]
    : [arr.slice(0, i), ...spliton(v, arr.slice(i+1))]


console.log(spliton('d', arr))

1 Comment

Honestly, OP should remove the accepted mark from my answer and make this one the accepted answer. It's technically a re-usable one-liner that's much, much more readable as well! +1
1

Here's a functional encoding that works for any iterable input (including arrays)

const None =
  Symbol ()

const prepend = (xs, x) =>
 [ x ] .concat (xs)

const split = (f, [ x = None, ...xs ], then = prepend) =>
  x === None
    ? then ([], [])
    : split
        ( f
        , xs
        , (l, r) =>
            f (x)
              ? then (prepend (l, r), [])
              : then (l, prepend (r, x))
        )

const data = 
  [ 'something', 'else', 'and', 'd', 'more', 'things', 'in', 'the', 'd', 'array', 'etc' ]

console .log
  ( split (x => x === 'd', data)
  )

// [ [ 'something', 'else', 'and' ]
// , [ 'more', 'things', 'in', 'the' ]
// , [ 'array', 'etc' ]
// ]

And an optimization that works for any array-like input

const prepend = (xs, x) =>
 [ x ] .concat (xs)

const split = (f, xs = [], i = 0, then = prepend) =>
  i >= xs.length
    ? then ([], [])
    : split
        ( f
        , xs
        , i + 1
        , (l, r) =>
            f (xs[i])
              ? then (prepend (l, r), [])
              : then (l, prepend (r, xs[i]))
        )

const data = 
  [ 'something', 'else', 'and', 'd', 'more', 'things', 'in', 'the', 'd', 'array', 'etc' ]

console .log
  ( split (x => x === 'd', data)
  )

// [ [ 'something', 'else', 'and' ]
// , [ 'more', 'things', 'in', 'the' ]
// , [ 'array', 'etc' ]
// ]

Both implementations are O(n).

Comments

0

If you do not care about mutating the array this is also pretty trivial with while and Array.shift:

let r = [[]], data = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"]

while(data.length) {
  let item = data.shift()
  item != 'd' ? r[r.length-1].push(item) : r.push([]) 
}

console.log(r)

And if you do then even shorter with Array.reduce:

let arr = ["something", "else", "and", "d", "more", "things", "in", "the", "d", "array", "etc"]

let f = arr.reduce((r,c) => (c!='d' ? r[r.length-1].push(c) : r.push([]),r),[[]])

console.log(f)

The idea in both is to start with [[]] and then the only check you need is if the current element of the iteration is d and if so push new array or push to r[r.length-1] which is the previous sub array.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.