简体   繁体   中英

Javascript Array String Word Wrap Problem --- Permutation of Strings in Order and A Given Array Length

I need to write a JS function which takes 1.an array of strings and 2.a new array length and returns every possible grouping of the strings (in order) in a new array.

This will help solve a text word wrap problem for a series of designs. That is an important detail since I do not need every single permutation of the set, the only correct solutions will be in correct order or sequence and words joined to the same array element will be separated by a single space. I have been working on a recursive solution but have not had any luck so for.

For example

everyPermutation([A,B,C,D,E], 4);

function everyPermutation(arr, length) {
...
}

would return

[
  ['A B', 'C', 'D', 'E'],
  ['A', 'B C', 'D', 'E'],
  ['A', 'B', 'C D', 'E'],
  ['A', 'B', 'C', 'D E']
]

Here is a recursive implementation. At each level of recursion you iterate the possibilities for the first chunk. The recursive call is for producing all the possibilities with that chunk removed, and with a chunk count that is one less:

 function partitions(arr, length) { if (length === arr.length) return [arr]; // shortcut if (length === 1) return [[arr.join(" ")]]; // base case let results = []; for (let firstlen = arr.length - length + 1; firstlen > 0; firstlen--) { let prefix = arr.slice(0, firstlen).join(" "); results.push(...partitions(arr.slice(firstlen), length - 1) .map(result => [prefix, ...result])); } return results; } // Example from question console.log(partitions(["A","B","C","D","E"], 4)); // Example from comments console.log(partitions(["A","B","C","D","E"], 3));

Another implementation, written in the style I prefer of expressions over statements, might look like this:

 const slicedCombos = (n, xs) => n == xs.length ? [xs.map (x => [x])] : [... Array (xs .length)] .map ((_, n) => xs .slice (0, xs .length - n)) .flatMap ( pf => slicedCombos (n - 1, xs .slice (pf .length)) .map (perm => [pf, ...perm]) ) const partitions = (n, xs) => slicedCombos (n, xs) .map (xss => xss .map (xs => xs .join(' '))) console .log (partitions (4, ['A', 'B', 'C', 'D', 'E'])) console .log (partitions (3, ['A', 'B', 'C', 'D', 'E']))
 .as-console-wrapper {max-height: 100% !important; top: 0}

This separates the problem of slicing and dicing the original array from that of formatting the output. The former seems more likely to be reusable.

In practice, I would probably extract a helper function for listing the prefixes of an array, again because I think it could be reusable. That would look like this:

const prefixes = (xs) =>
  [... Array (xs .length)] .map ((_, n) => xs .slice (0, n + 1))  

const slicedCombos = (n, xs) => 
  n == xs.length
    ?  [xs.map (x => [x])]
  : prefixes (xs) .reverse() .flatMap (
      pfx => slicedCombos (n - 1, xs .slice (pfx .length)) .map (perm => [pfx, ...perm])
    )

The additional reverse is to order these results as described. If the order didn't matter, we could skip it.

I don't name the main function or the reusable helper function "everyPermutation" because that's a bit misleading. "permutations" means something different. While I chose "partitions", that's a little too generic, and a better name would be useful.

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