Incorporating map reduce and other methods, is it possible to locate the initial item that meets specific criteria within a nested array and terminate the search upon discovery?

How can you locate the first item that meets specific criteria within a nested array and halt the search once it is found?

In a one-dimensional array, this task can be accomplished with the Array.find function. But how would you tackle this in a two-dimensional array, or even better, in an n-dimensional array?

I am exploring a streamlined solution using ES6 and array functions like find, map, and reduce, rather than relying on traditional loops and variables to maintain state (see old-school approach below).

The data structure might resemble:

const data = [
  {arr: [{val:6,name:'aaa'},{val:4,name:'bbb'},{val:8,name:'ccc'}]},
  {arr: [{val:3,name:'mmm'},{val:5,name:'nnn'},{val:9,name:'ppp'},{val:5,name:'ooo'}]}
]

I am aiming for a method akin to array.find, including its predicate/testing function, but with the ability to delve deep and identify the first occurrence of val=5, for instance. Given the example data above, I anticipate retrieving the element named 'nnn' (not 'ooo') and terminating the process upon finding the initial match. Just like Array.find, my goal is to cease processing the remaining data after encountering a matching item.

An unexciting conventional technique involves utilizing a loop like so:

let found
// iterating through all entries in the outer array
for (const d of data) {
  // searching for a matching item in the inner array.
  // utilizing array.find ensures halting at the first match
  const theItem = d.arr.find(item => {
    return myPredicate(item)
  })
  // breaking out of the loop when a match is obtained
  if (theItem) {
    found = theItem
    break
  }
}
// returning the found item (may be undefined)
return found

A potential workaround could involve employ find() and some(), as shown in this response ES6 - Finding data in nested arrays. However, employing find on the external array results in obtaining the first item within the outer data array, whereas I require an element from the internal arr array.

const outer = data.find(d => {
  return d.arr.some(item => {
    return myPredicate(item)
  })
})

Subsequently, I would have to revisit outer to pinpoint the item within outer.arr, similar to:

outer.arr.find(item => myPredicate(item))

This methodology seems inefficient since some(...) already identified the matching inner item!

I anticipated this task to be straightforward, yet for some reason, it has posed a challenge for me.

I also explored the traverse library (https://www.npmjs.com/package/traverse), although it appears more focused on traversing an entire tree instead of pausing and returning upon discovering a particular node.

Who’s up for a challenge? ;)

Answer №1

An easy but not so elegant solution would be to store the matching item in an outer variable once it is found:

let foundNested;
data.some(subarr => (
  subarr.some((item) => {
    if (myPredicate(item)) {
      foundNested = item;
      return true;
    }
  });
});

Alternatively, you could utilize .reduce to avoid using an outer variable:

const myPredicate = ({ val }) => val === 5;
const data = [
  {arr: [{val:6,name:'aaa'},{val:4,name:'bbb'},{val:8,name:'ccc'}]},
  {arr: [{val:3,name:'mmm'},{val:5,name:'nnn'},{val:9,name:'ppp'},{val:5,name:'ooo'}]}
];

const found = data.reduce((a, { arr }) => (
  a ||
  arr.find(myPredicate)
), null);
console.log(found);

The issue with using reduce is that it doesn't short-circuit - it will iterate over the entire outer array regardless. For proper short-circuiting, consider using a for..of loop:

const data = [
  {arr: [{val:6,name:'aaa'},{val:4,name:'bbb'},{val:8,name:'ccc'}]},
  {arr: [{val:3,name:'mmm'},{val:5,name:'nnn'},{val:9,name:'ppp'},{val:5,name:'ooo'}]}
];
function findNested(outerArr, myPredicate) {
  for (const { arr } of outerArr) {
    for (const item of arr) {
      if (myPredicate(item)) {
        return item;
      }
    }
  }
}

const myPredicate = ({ val }) => val === 5;
console.log(findNested(data, myPredicate));

Answer №2

To create a custom find function without a predicate but with a callback that produces results, follow this code:

function find(iterable, callback) {
    for (const value of iterable) {
        const result = callback(value);
        if (result !== undefined)
            return result;
    }
}

Then, you can use it like this:

const data = [
  {arr: [{val:6,name:'aaa'},{val:4,name:'bbb'},{val:8,name:'ccc'}]},
  {arr: [{val:3,name:'mmm'},{val:5,name:'nnn'},{val:9,name:'ppp'},{val:5,name:'ooo'}]}
];
console.log(find(data, ({arr}) => find(arr, o => o.val == 5 ? o : undefined)));

If you need to retrieve all matching results, consider using the flatMap method:

data.flatMap(({arr}) => arr.filter(({val}) => val == 5));

Answer №3

Of course, I'm all in. This situation has room for improvement. However, it should get the job done. Imagine you're looking for an object with an id of 5 within a multidimensional array.

const arr = [[[{id: 1}], [{id: 2}]], [[{id: 3}]], [[{id: 4}], [{id: 5}], [{id: 6}]]]
function findObject (obj) {
if (Array.isArray(obj)) {
const len = obj.length
for (let i = 0; i < len; i++) {
const found = findObject(obj[i])
if (found) {
return found
}
}
} else if (obj.id === 5) { // Enter your search criteria here.
return obj
}
}

const obj = findObject(arr)
console.log('obj: ', obj)

Answer №4

Although this code appears to be functioning properly, I believe it could benefit from some refactoring in order to improve its cleanliness. The use of the 'found' variable outside of the main block, which is assigned from within a nested find block, adds unnecessary complexity. While the current version is an improvement, there may still be room for further enhancements. What are your thoughts on this?

let found
data.find(d =>
  d.arr.find(item => {
    found = myPredicate(item) ? item : void 0
    return found !== void 0
  }) !== void 0
)
return found

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

Verify if the element has been clicked and chosen before proceeding to the next action

It has come to my attention that users are able to submit answers without actually making any selections from a menu of buttons. This results in an empty array being printed to the console, which is not the intended behavior. I am seeking a solution that ...

Tips for dynamically passing a path in require method in JavaScript

I am facing an issue while trying to set require() with a dynamic path myPath: let myPath = './myDynamicModule'; require( { myPath } ) However, I keep encountering the following error: error: bundling failed: myComponent.js: myComponent.js ...

Customize the appearance of your apps script using conditional formatting to highlight values that are

https://i.stack.imgur.com/L1KFZ.png I need to create an array of all 50 US states based on the abbreviations in a column. The goal is to compare each cell against the array and if it doesn't match, format it red. After researching conditional format ...

"Is there a way to initiate a Date Picker with the current date using Javascript in an AngularJS application

I am having an issue with my date picker where the month starts from the current month and goes up to the next 12 months. However, I only want to display dates from the current date to the last date of the current month. I believe I need to set a limit so ...

Having trouble parsing the JSON response, even though it's not empty it still comes back as undefined

My current dilemma involves using JSON codes to send a post request, retrieve data, and then parse it (see example below). $.ajax({ url: '/test', type: 'post', data: { id : "1"}, dataType: 'json', ...

Utilize the lodash times method for indexing

I have a requirement to duplicate a component "n" number of times. I achieved this by utilizing the lodash method called "times". However, I encountered an issue with assigning an index as a key for the generated components. Below is the snippet of code t ...

Transforming a string into the date input type layout

I am working with an HTML input type of "datetime-local" that I save in a Datetime field within a MS SQL Server database. Upon loading the edit page, I aim to populate this HTML date field with the value retrieved from the database. Upon examining the va ...

I'm only seeing output on two of my input boxes - what's going on?

Currently in the process of learning JQUERY/HTML, I decided to create a shopping cart. My goal is to display the subtotal, tax, shipping costs, and total cost in input boxes. While I successfully displayed the sub total and shipping cost, I encountered an ...

Is it possible to utilize a pointer as a form of an array?

Recently, I encountered an issue with my code that has me puzzled. Here's the scenario: int *userInput; //array place holder //int max; //Max variable for later use in comparisons int start = 0; //inital starting point value for loops int endUserInpu ...

npm installs a multitude of dependencies

Recently, I purchased an HTML template that includes various plugins stored in a directory named bower_components, along with a package.js file. While trying to add a new package that caught my interest, I opted to utilize npm for the task. Upon entering ...

Issue with ngFor displaying only the second item in the array

There are supposed to be two editable input fields for each section, with corresponding data. However, only the second JSON from the sample is being displayed in both sections. The JSON in the TypeScript file appears as follows: this.sample = [ { "se ...

Preventing Component Duplication in Vue.js: Tips to Avoid Displaying Two Instances on the Screen

After developing a Vue app in VS Code, I encountered an issue where the home component was rendered twice on the screen when attempting to run the application. Below is a screenshot of the resulting homepage: Here is the code from Home.vue: ...

Updating the footer of a React application using Material UI Grid

Having some trouble customizing the footer in a React Material-UI grid. Refer to the image below for details. Any ideas on how to change the: 1 row selected? Thank you! ...

Updating an image using AJAX and Flask

I have a situation in HTML where I need to constantly update an image file with the latest images that come in. Below is the Flask code snippet: @app.route('/uploads/update_file', methods=['GET', 'POST']) def update_file(): ...

What is the correct way to effectively clear an interval in my particular situation?

Check out the live demo here $(".moving_container").mouseenter( function(){ clearInterval(timer); } ).mouseleave(function(){ timer = getInterval(slideWidth,slideHeight,slideLength) }); I'm curren ...

Ways to fix the issue of an unspecified user error in authjs

Having trouble with creating a web token using passport LocalStrategy and passport-jwt. I keep getting a user undefined error in auth.js ****401 Unauthorized**** (if (!user) { return res.json(401, { error: 'message' });}). How can I fix this issu ...

Guide on how to align the bootstrap popover's arrow tip with a text field using CSS and jQuery

I have tried multiple solutions from various questions on Stack Overflow, but I am still struggling to position the arrow tip of the bootstrap popover correctly. html: <input type = "text" id="account_create"/> js: $('.popov ...

Looking to store HTML form data in MongoDB and showcase the saved information on a webpage using AngularJS, ExpressJS, and NodeJS

Client-side : Example HTML Code <body ng-controller="myAppController"> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <h1>person details</h1> </div> ...

Interacting with Command Line in NodeJS using Express

Is there a way to connect with the JS environment of an Express app through the command line, just like using a browser's console? For instance, let's assume Express is up and running. Can I inspect variables by logging them to the terminal usin ...

Reading an XML file to locate items nested within the same bracket

Within my JavaScript function, I am utilizing the following code to extract data from an XML file: var title = $(this).children('Title').text(); This snippet of code successfully retrieves the content under the <Title> tags: <Title> ...