简体   繁体   English

如何使用Lodash通过多个潜在属性过滤对象集合?

[英]How to use Lodash to filter a collection of objects by multiple potential properties?

I'm building a simple filtering UI. 我正在构建一个简单的筛选UI。 I want to filter a collection of objects using a logical AND, where the object's match depends on multiple potential values for each in an arbitrary set of keys. 我想使用逻辑AND过滤对象集合,其中对象的匹配取决于任意键集中每个对象的多个潜在值。 The objects to filter look like: 要过滤的对象如下所示:

collection = [
  {
    'Kind': 'Instrument',
    'Color': 'Orange',
    'Shape': 'Square',
    'Name': 'Foobar',
  },
  ...
]

And the user's point-and-click filter results look like: 用户的点击过滤器结果如下所示:

filter = {
  'Color': ['Red', 'Orange', 'Yellow'],
  'Shape': ['Circle']
}

In this case, I want to filter the collection to all objects that are: 在这种情况下,我想将集合过滤为以下所有对象:

  • Color is Red OR Orange OR Yellow 颜色是红色或橙色或黄色
  • AND Shape is Circle AND形状为圆形

The filter object has an arbitrary number of keys, so I can't easily write a manual filter function like this: filter对象具有任意数量的键,因此我不能轻松地编写这样的手动过滤器功能:

results = _.filter(collection, item => {
  return _.includes(['Red', 'Orange', 'Yellow'], item['Color']) && _.includes(['Circle'], item['Shape'])
})

What is the cleanest way to achieve this using Lodash? 使用Lodash做到这一点的最干净的方法是什么? Do I need to loop over each key in filter for each item in collection inside of my _.filter or is there a better way? 我是否需要为_.filter内部collection每个项目遍历filter每个键,还是有更好的方法?

PS I don't have the right language to speak about what I'm trying to do. PS:我没有正确的语言来谈论我要做的事情。 What are the best keywords to describe this question? 描述这个问题的最佳关键字是什么?

You are very close: 您非常接近:

 const collection = [ // Won't be in the output. { 'Kind': 'Instrument', 'Color': 'Orange', 'Shape': 'Square', 'Name': 'Foobar', }, // Will be in the output. { 'Kind': 'Instrument', 'Color': 'Orange', 'Shape': 'Circle', 'Name': 'Foobar', }, // Won't be in the output. { 'Kind': 'Derp', 'Color': 'NoWorky', 'Shape': 'HAHAHAHA', 'Name': 'uWin', } ]; const filter = { 'Color': ['Red', 'Orange', 'Yellow'], 'Shape': ['Circle'] }; const results = _.filter(collection, (item) => { return _.chain(filter) .keys() .reduce((currentBoolean, next) => { console.log(currentBoolean, next, filter[next], item[next]); return _.isNil(item[next]) ? currentBoolean : _.includes(filter[next], item[next]) && currentBoolean; }, true) .value(); // This doesn't work because you're trying to do arbitrary keys. // return _.includes(filter.Color, item.Color) && _.includes(filter.Shape, item.Shape)); }); console.log(results); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script> 

Applying a bit of functional style, I'd just do as you're stating. 应用一些功能样式,就像您说的那样。 Hopefully the code is explanatory enough, otherwise please let me know and I'll try to clarify things (with lodashfp would be a little clearer though I don't think you're using it). 希望代码足够说明性,否则请让我知道,我将尽力澄清事情(使用lodashfp会更清晰一些,尽管我认为您没有使用它)。

Hope it helps. 希望能帮助到你。

 const isValIncluded = item => (value, key) => _.includes(value, _.get(item, key, null)); const isValid = filters => item => _.every(filters, isValIncluded(item)); const filterCol = coll => filters => _.filter(coll, isValid(filters)); const collection = [{ 'Kind': 'Instrument', 'Color': 'Orange', 'Shape': 'Square', 'Name': 'Foobar', }, { 'Kind': 'Instrument', 'Color': 'Orange', 'Shape': 'Circle', 'Name': 'Foobar', }, { 'Kind': 'XXX', 'Color': 'YYY', 'Shape': 'ZZZ', 'Name': 'FFF', }, { 'Kind': 'Instrument', 'Color': 'Red', 'Shape': 'Circle', 'Name': 'Foobar', } ]; const filters = { 'Color': ['Red', 'Orange', 'Yellow'], 'Shape': ['Circle'] }; console.log(filterCol(collection)(filters)); 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script> 

You could simplify using 您可以简化使用

results = _.filter(collection, item => item.Shape==='Circle' && _.includes(['Red', 'Orange', 'Yellow'], item.Color));

Probably a good idea to avoid confusion by naming the filter object differently here. 通过在此处不同地命名filter对象来避免混淆可能是一个好主意。

This will work for a list of items where the givenProperty you want to filter on is either a string like 'doorColour' or an array of strings representing the path to the givenProperty like ['town', 'street', 'doorColour'] for a value nested on an item as town.street.doorColour. 这将适用于项目列表,在该列表中,您要过滤的namedProperty是“ doorColour”之类的字符串,或者是表示给定Property的路径的字符串数组,例如['town','street','doorColour']嵌套在项目上的值是town.street.doorColour。

It also can filter on more than one value so you could you just need pass in an array of substrings representing the string values you want to keep and it will retain items that have a string value which contains any substring in the substrings array. 它还可以过滤多个值,因此您只需要传入表示要保留的字符串值的子字符串数组即可,它将保留具有字符串值的项目,该字符串值包含在substrings数组中的任何子字符串。

The final parameter 'includes' ensures you retain these values if you set it to false it will exclude these values and retain the ones that do not have any of the values you specified in the substrings array 最终参数“ includes”可确保您保留这些值(如果将其设置为false),它将排除这些值并保留那些没有在子字符串数组中指定的值的值。

import { flatMap, path } from 'lodash/fp';

const filteredListForItemsIncludingSubstringsOnAGivenProperty = (items, givenProperty, substrings, including=true) => flatMap((item) =>
substrings.find((substring) => path(givenProperty)(item) && path(givenProperty)(item).includes(substring))
  ? including
    ? [item]
    : []
  : including
  ? []
  : [item])(items);

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

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