简体   繁体   English

使用map reduce等,如何在嵌套数组中找到匹配特定条件的第一项,并在找到后停止?

[英]Using map reduce etc, how would you find the first item matching a certain criteria in a nested array, and stop once found?

How would you find the first item matching a certain criteria in a nested array, and stop once found? 您如何在嵌套数组中找到匹配特定条件的第一项,并在找到后停止?

In a 1D array, this is what the Array.find function is for, but how would you do it for a 2D array, and, even neater, for n-dimension array? 在1D数组中,这就是Array.find函数的作用,但是对于2D数组,甚至对于n维数组,您将如何处理呢?

Also, I'm trying to come up with a neat solution using es6 and array functions such as find, map, reduce etc, rather than using more traditional loops and variables to maintain state (see one such old-school solution below). 另外,我正在尝试使用es6和数组函数(例如查找,映射,归约等)提出一种简洁的解决方案,而不是使用更多的传统循环和变量来维护状态(请参见下面的一种此类老派解决方案)。

The data may look something 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'}]}
]

I'm hoping I can do something similar to array.find (and its predicate / testing function), but I need to go deeper and find eg the first item with val=5. 我希望我可以做一些类似于array.find(及其谓词/测试功能)的事情,但是我需要更深入地查找例如val = 5的第一项。 For the data above, I'd expect to get the item with name 'nnn' (not 'ooo'), and have the process end once the first item is found. 对于上面的数据,我希望获得名称为“ nnn”(而不是“ ooo”)的项目,并在找到第一个项目后结束该过程。 Similar to Array.find, I want to avoid processing the rest of the data once a matching item is found. 与Array.find类似,一旦找到匹配项,我想避免处理其余数据。

One boring old way to do it would be something like this, with a loop, but that's... boring, and not as neat as the lovely array functions :) 一种无聊的旧方法可以做到这一点,有一个循环,但这很无聊,不像可爱的数组函数那样整洁:)

let found
// loop through all data entries in the outer array
for (const d of data) {
  // attempt to find a matching item in the inner array.
  // using array.find means we stop at the first match, yay!
  const theItem = d.arr.find(item => {
    return myPredicate(item)
  })
  // we also need to break out of the loop. ugh!
  if (theItem) {
    found = theItem
    break
  }
}
// return what we found (may be undefined)
return found

Now, I realise that I can do something with find() and some(), say, similar to the answer here ES6 - Finding data in nested arrays , but the problem is that using find on the outer array means that we get back the first item of the outer data array, whereas I want an item from the inner arr array. 现在,我意识到我可以使用find()和some()做一些事情,例如,类似于此处的答案ES6-在嵌套数组中查找数据 ,但是问题是,在外部数组上使用find意味着我们取回了外部数据数组的第一项,而我想要内部arr数组的项。

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

I would then have to process outer AGAIN to find the item in outer.arr, something like 然后,我将不得不处理外部AGAIN才能在external.arr中找到该项目,例如

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

This doesn't sit well with me, as the call to some(...) has already gone through and found the matching inner item! 这对我来说不太好,因为对some(...)的调用已经通过并找到了匹配的内部项目!

I thought this would be straight forward, and maybe it is, but for one reason or another I got stuck on this little challenge. 我以为这很简单,也许是,但是出于一个或另一个原因,我陷入了这个小挑战。

I've also looked at the nice traverse library ( https://www.npmjs.com/package/traverse ), but again that seems to be more about traversing through a whole tree rather than stopping and returning once a particular node is found. 我也看过了漂亮的遍历库( https://www.npmjs.com/package/traverse ),但同样,这似乎更多地是遍历整个树,而不是在找到特定节点后停止并返回。

Anyone up for a challenge? 有人要挑战吗? ;) ;)

The easiest (though slightly ugly) solution would be to assign the matching item to an outer variable when found: 最简单(虽然有点难看)的解决方案是在找到匹配item将其分配给外部变量:

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

You might use .reduce to avoid assigning to an outer variable: 您可以使用.reduce避免分配给外部变量:

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

Problem is, the reduce won't short-circuit - it'll fully iterate over the outer array regardless. 问题是, reduce不会短路-它将完全遍历外部数组。 For true short-circuiting, I think I'd prefer using a for..of loop: 对于真正的短路,我想我更喜欢使用for..of循环:

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

You'll want to write your own find function that doesn't take a predicate but a result-producing callback: 您将要编写自己的find函数,该函数不带谓词,而是产生结果的回调:

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

With that, you can write 这样,您可以编写

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

Alternatively, if you want to get all results, flatMap is the perfect tool: 另外,如果您想获得所有结果, flatMap是理想的工具:

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

Sure, why not. 当然可以,为什么不呢。 I'm up for it. 我愿意。 This can probably be improved upon. 这可能可以改进。 But this will work. 但这会起作用。 Let's say you are trying to find an object with id of 5 in a multidimensional array. 假设您正在尝试在多维数组中找到ID为5的对象。

  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) { // Put your search condition here.
      return obj
    }
  }

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

This seems to work, but, in my opinion, it's still not clean with that 'found' variable sitting outside the main block and being assigned from inside the nested find block. 这似乎可行,但我认为,将“找到”变量放在主块外部并从嵌套的find块内部进行分配仍然不干净。 It's better though. 更好。 Thoughts? 思考?

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 你会如何解决这个问题? 使用reduce方法计算此数组中嵌套的object值的总和? - How would you tackle this? using the reduce method to calculate the sum of a nested object value in this array? Javascript:使用reduce / map等创建具有特定值的对象数组 - Javascript: Create array out array of objects with certain value using reduce / map etc 使用 Puppeteer,您将如何通过一系列链接 map 将 go 到阵列中的第一个链接关闭该选项卡,然后重复? - Using Puppeteer, how would you map through an array of links where it would go to the first link in the array close that tab, and repeat? 如何使用 map 在 jsx 中渲染某个项目一次? - how to render a certain item once in jsx using map? 您将如何使用猫鼬填充嵌套在数组对象中的引用? - How would you populate references nested in an object of an array using mongoose? 如何过滤map嵌套数组项,同时检查要过滤的项是否满足一定条件? - How to filter and map nested array items while checking whether the to be filtered items meet certain criteria? 如何使用ImmutableJS Map设置嵌套数组中项的属性? - How to set a property of an item in nested array using ImmutableJS Map? 您将如何在 arguments 而不是特定数组或 object 上使用.reduce()? - How would you use .reduce() on arguments instead of a specific array or object? 你将如何减少这个数组? (阅读说明以获取更多信息) - How would you reduce this array? (Read description for more info) 您将如何使用 map 从对象数组中的每个对象中查找特定值 - How would you use map to find a specific value from each object in an array of objects
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM