Discover every possible path combination

Looking to flatten an array of 1D arrays and simple elements, reporting all combinations until reaching a leaf "node." For example:

// Given input array with single elements or 1D arrays:
let input = [1, 2, [3, 4], [5, 6]];

The unfolding process splits paths for each encountered array into as many elements as the array contains:

// current result = [1, 2]
// remaining input [[3, 4], [5, 6]]
// ->
// current result = [ [1, 2, 3], [1, 2, 4] ]

// current result = [ [1, 2, 3], [1, 2, 4] ]
// remaining input [[5, 6]]
// ->
// final result = [ [1, 2, 3, 5], [1, 2, 4, 5], [1, 2, 3, 6], [1, 2, 4, 6] ]

Struggling with handling special cases like:

let input1 = [1, 2, 3]; // No nested arrays
let input2 = [];        // Empty input

Attempted to build the result backwards using .pop, but facing issues with typescript compilation:


function flatPath(input, result = [[]]) {
    while (input.length) {
        const last = input.pop();

        if (Array.isArray(last)) {
            result = flatPath(last, [...result, ...result]);
        } else {
            for (let ar of result) {
                result.push(last);
            }
        }
    }
    return result;
}

let result = flatPath([1, 2, [3, 4], [2, 5, 6] ]);

console.log(result);

Encountering errors like 'Parameter 'input' implicitly has an 'any' type' and 'Argument of type 'any' is not assignable to parameter of type 'never'.

Is there a better way to achieve this or can you help identify the issue in my code?

Answer №1

By implementing a recursive function and using the map() method, it is possible to handle the aggregation of results without the need to pass down a context array explicitly. This approach involves adding each non-array element from the previous call to the sub-arrays returned by deeper calls.

function flatPath([next, ...rest]) {
  if (next === undefined) {
    return [[]];
  }

  if (Array.isArray(next)) {
    const temp = [];
    for (const n of next) {
      temp.push(...flatPath([n, ...rest]));
    }
    return temp;
  }

  return flatPath(rest).map(t => [next, ...[].concat(t)]);
}


// Test output
for (const test of [
  [1, 2, [3, 4], [5, 6]],
  [1, 2, 3], 
  [[1, 2, 3], 4], 
  []
]) {
  let result = flatPath(test);
  console.log(JSON.stringify(result));
}

 // [[1,2,3,5],[1,2,3,6],[1,2,4,5],[1,2,4,6]]
 // [[1,2,3]]
 // [[1,4],[2,4],[3,4]]
 // [[]]

If there is an undefined element in the input array or an empty array is passed as input, the above approach may fail. To address this issue, modifications were made to check against the length of ...rest and add checks for empty arrays.

function flatPath(input) {
  if (input.length === 0) {
    return [];
  }

  const [next, ...rest] = input;

  if (Array.isArray(next)) {
    const temp = [];
    for (const n of next) {
      const tail = rest.length
        ? flatPath([n, ...rest])
        : flatPath([].concat(n));

      temp.push(...tail);
    }
    return temp;
  }

  return rest.length
    ? flatPath(rest).map(t => [next, ...[].concat(t)])
    : [next];
}

// Test output
for (const test of [
  [1, 2, [3, 4], [5, 6]],
  [1, 2, 3], 
  [[1, 2, 3], 4], 
  []
]) {
  let result = flatPath(test);
  console.log(JSON.stringify(result));
}

 // [[1,2,3,5],[1,2,3,6],[1,2,4,5],[1,2,4,6]]
 // [[1,2,3]]
 // [[1,4],[2,4],[3,4]]
 // [[]]

Answer №2

Employing a generator function in this scenario proves to be highly effective, as it provides the ability to yield results at the lower levels of the hierarchy (whose quantity is not known beforehand), rather than dealing with aggregating data back up through the recursion stack.

const flatPath = function*(array, prefix = []) {
  const next = array[0];
  if (next === undefined) { // base case
    yield prefix;
  } else if (next instanceof Array) {
    for (let item of next) {
      yield * flatPath(array.slice(1), [...prefix, item]);
    }
  } else {
    yield * flatPath(array.slice(1), [...prefix, next]);
  }
};

const result = Array.from(flatPath([1, 2, [3, 4], [2, 5, 6]]));
console.log(result);

/* output:
[
  [ 1, 2, 3, 2 ],
  [ 1, 2, 3, 5 ],
  [ 1, 2, 3, 6 ],
  [ 1, 2, 4, 2 ],
  [ 1, 2, 4, 5 ],
  [ 1, 2, 4, 6 ]
]
*/

Answer №3

I was able to successfully compile your code, but it seems to be stuck in an infinite loop. The issue lies within this section;

        for (let ar of result) {
            result.push(last);
        }

The problem arises from pushing elements to the result array while simultaneously iterating over it. This creates an endless loop.

To resolve this problem, you should create a separate array to hold the new elements and then combine it with the result array outside of the loop.

The following modification should work correctly;

        const newElements = [];
        for (let ar of result) {
            newElements.push(last);
        }
        result = result.concat(newElements);

Answer №4

The code snippet provided below showcases an updated version of a particular algorithm, reimagined independently from the original answer by pilchard. This new rendition focuses on streamlining the code and leveraging pure expressions over statements:

const process = ([xs, ...xss], rs = xs !== undefined && process(xss)) =>
  xs === undefined
    ? [[]]
  : Array.isArray(xs)
    ? xs.flatMap(x => rs.map(r => [x, ...r]))
  : rs.map(r => [xs, ...r]);

const input = [1, 2, [3, 4], [5, 6]]

console .log(JSON.stringify(process (input)))

The revised implementation also addresses potential concerns regarding the presence of undefined values within the input dataset. To mitigate this issue, one could introduce a unique symbol as showcased in the following example:

const None = Symbol()

const process = ([xs = None, ...xss], rs = xs !== None && process(xss)) =>
  xs === None
    ? [[]]
  : Array.isArray(xs)
    ? xs.flatMap(x => rs.map(r => [x, ...r]))
  : rs.map(r => [xs, ...r])

const input = [1, 2, [3, 4], [5, 6]]

Ultimately, the decision to implement such handling mechanisms for undefined values depends on the specific requirements of your project.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to specifically retrieve the number of likes or followers from Facebook, Twitter, Instagram, and Snapchat in order to display them on my HTML website?

Can you provide guidance on how to obtain the total numbers of likes or followers for our Facebook, Twitter, Instagram, and Snapchat pages without including the plugin boxes or buttons? We are solely interested in the actual numbers. Would this be feasibl ...

How to verify the parent nodes in a jstree

I have implemented a two state jstree. However, I am encountering an issue where it is not possible to select any other node in relation to a node. My goal is that when I click on a specific node, all of its parent nodes should also be checked. Any assist ...

Is there any method to avoid the hassle of constantly adjusting margins and paddings on my one-page website?

One issue I encountered was that the buttons weren't scrolling me to the top of the anchor, instead scrolling too far into the section due to the fixed navbar overlapping. I tried solving it with margins and paddings but believe there must be a simpl ...

From JSON to JavaScript transformations

Currently, I am attempting to utilize JSON with data retrieved from the server by implementing this PHP script: include("db_connect.php"); mysql_connect($host,$username,$password); @mysql_select_db($database) or die( "Unable to select database"); $resu ...

Utilizing AngularJS with multiple dynamically generated forms on a webpage: Extracting data from each form

I am facing a challenge with a web page I have created that uses PHP to generate forms for updating information via a REST API on a remote server. The structure of the forms is as follows: Textfield: name textfield: description select: type (ng-model="se ...

What is the best method for caching inline SVG in web browsers?

Over the years, SVGs have remained popular due to their scalability. One key advantage of inline SVG is the ability to manipulate it with CSS and JS. Additionally, using the <use> tag allows for referencing the original element when repeating the sam ...

Validation of forms - Must include one particular word from a given set

I am in the process of utilizing Javascript to validate an input field with the specific formatting requirements outlined below: "WORD1,WORD2" The input must contain a comma separating two words, without any spaces. The first word (WORD1) can be any word ...

How does the object scope receive, process, and prepare the update for display within the AngularJs view?

AngularJS allows for data-binding to easily display immediate data in our View. This is made possible by the object scope, which acts as a connector between the Logic Code and The View. Additionally, AngularJs supports two-way binding. My question is: H ...

Is it feasible to access a service instance within a parameter decorator in nest.js?

I am looking to replicate the functionality of Spring framework in nest.js with a similar code snippet like this: @Controller('/test') class TestController { @Get() get(@Principal() principal: Principal) { } } After spending countless ho ...

How can TypeScript be forced to output a specific data type?

I've created a generic "fetcher" function that is designed to handle different types of entities. However, I'm encountering an issue where TypeScript is inferring the return type based on all possible conditions within the function. Is there a w ...

In the game of rock paper scissors, the icons become unclickable once you achieve a score of 5

I have created a Rock Paper Scissors game where the score resets if either the user or computer reaches 5 points, but I want to prevent any further play after hitting 5. Currently, the game displays a message and reloads after 5 seconds, during which tim ...

Organize the table data based on time

My website specializes in offering cell phone rental services. Users can visit the site to view the available devices that we have. I designed the display of these devices using a table format and components from "@mui/material". One of the columns in thi ...

The jQuery element selection feature is not functioning

Within my HTML file, there lies a table with an empty tbody that is dynamically filled by jQuery once the document is fully loaded. The content of the table body is generated using a PHP script, where jQuery fetches the data via a simple GET request. Belo ...

Manipulating HTML content with jQuery

Review the code snippet provided below <div id="post-1234"> <h3><a href="some link">text</a></h3> <div> <div> <div> <span class"Event-Date">Event Date 1</span> </div> < ...

Setting up parameters and arguments in Vuex mutations: A guide

I am currently developing a todo list application using Vue.js, Vuex, and Firebase. The functionality of the app seems to be in working order with the Store file effectively managing the retrieval and display of entered todo items to and from Firestore. Ho ...

Risky assignment of an 'any' value" encountered while implementing react-transition-group in TypeScript

Encountering @typescript-eslint errors while implementing the Transition component from react-transition-group. Implemented the official React Transition Group example for JS in a TypeScript + ESLint project, resulting in the error message: Unsafe assignm ...

Despite its presence, @Input() remains undefined

Currently, I am engrossed in a project that aims to display information about movies. By utilizing @Input(), I am establishing a connection between the movie details from the parent component (movies) and the child component (movie-detail). Movies Parent ...

every cell should be painted a distinct hue

I need to create a 10x10 array in JavaScript and fill each cell in the 2D array with different colors using canvas. I have managed to do this part, but now I am stuck on how to check if each cell has a different color from its neighbors. Any suggestions ...

Creating cookies in R Shiny: Storing variables for future use

Writing a fixed string to cookies can be done easily using methods like Cookies.set(\'cookie_2\', \'value\', { expires: 7 }) (see tutorial here). However, saving the variable user to cookie_2 may require a different ...

Retrieve the following object in an array of objects using a specific property value in Javascript

I am working with an array of objects structured like this: orders: [ 0: { order_id: 234, text: 'foo' }, 1: { order_id: 567, text: 'bar' } ] Suppose I have the ID 234 and I want to find the next object in the a ...