简体   繁体   中英

JavaScript - String.split() but for Arrays?

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?

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 d s:

 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); 

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[++ai] = []) && a : a.arr[ai].push(c) && a, {arr: [[]], i: 0}).arr; console.log(result); 

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')); 

 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); 

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) 

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) 

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)) 

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) .

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM