简体   繁体   English

Lodash:按多个属性过滤嵌套对象

[英]Lodash: filter a nested object by multiple properties

Consider the following example: 考虑以下示例:

var products = {
    "Products": [{
        "Title": "A",
        "Categories": [{
            "Name": "Type",
            "Properties": ["Type 1", "Type 2", "Type 3"]
        }, {
            "Name": "Market",
            "Properties": ["Market 1", "Market 2", "Market 3", "Market 4"]
        }, {
            "Name": "Technology",
            "Properties": ["Tech 1", "Tech 2"]
        }]
    }, {
        "Title": "B",
        "Categories": [{
            "Name": "Type",
            "Properties": ["Type 1", "Type 3"]
        }, {
            "Name": "Market",
            "Properties": "Market 1"
        }, {
            "Name": "Technology",
            "Properties": ["Tech 1", "Tech 3"]
        }]
    }, {
        "Title": "C",
        "Categories": [{
            "Name": "Type",
            "Properties": ["Type 1", "Type 2", "Type 3"]
        }, {
            "Name": "Market",
            "Properties": ["Market 2", "Market 3"]
        }, {
            "Name": "Technology",
            "Properties": ["Tech 2", "Tech 3"]
        }]
    }]
}

I'm trying to filter products by their properties so consider I'm using an array to keep track of my selected filters: 我试图按产品属性过滤产品,因此考虑使用数组来跟踪所选过滤器:

var filters = ['Type 3', 'Tech 1'];

With these filters I would like to return product A and product B. 使用这些过滤器,我想退回产品A和产品B。

I currently have this: 我目前有这个:

var flattenedArray = _.chain(products).map('Categories').flatten().value();
var result= _.some(flattenedArray , ['Properties', 'Tech 1']);

But I'm stuck on how to combine the properties for a combined search. 但是我对如何组合属性进行组合搜索感到困惑。

Use _.filter() to iterate the products. 使用_.filter()迭代产品。 For each product combine the list of properties using _.flatMap() , and use _.intersection() and _.size() to find the amount of filters that exist in the categories. 对于每种产品,使用_.flatMap()组合属性列表,并使用_.intersection()_.size()来查找类别中存在的过滤器数量。 Compare that to the original number of filters, and return comparison's response. 将其与原始过滤器数量进行比较,然后返回比较的响应。

 var products = {"Products":[{"Title":"A","Categories":[{"Name":"Type","Properties":["Type 1","Type 2","Type 3"]},{"Name":"Market","Properties":["Market 1","Market 2","Market 3","Market 4"]},{"Name":"Technology","Properties":["Tech 1","Tech 2"]}]},{"Title":"B","Categories":[{"Name":"Type","Properties":["Type 1","Type 3"]},{"Name":"Market","Properties":"Market 1"},{"Name":"Technology","Properties":["Tech 1","Tech 3"]}]},{"Title":"C","Categories":[{"Name":"Type","Properties":["Type 1","Type 2","Type 3"]},{"Name":"Market","Properties":["Market 2","Market 3"]},{"Name":"Technology","Properties":["Tech 2","Tech 3"]}]}]}; var filters = ['Type 3', 'Tech 1']; var result = _.filter(products.Products, function(product) { return filters.length === _(product.Categories) .flatMap('Properties') .intersection(filters) .size(); }); console.log(result); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script> 

If I understand you question correctly, this code may help: 如果我正确理解您的问题,那么此代码可能会有所帮助:

_.filter(
    products.Products,
    product => _.difference(
        filters,
        _.chain(product.Categories).map(category => category.Properties).flatten().value()
    ).length === 0
);

It calculates a union of all properties for each product: 它计算每个产品的所有属性的并集:

_.chain(product.Categories).map(category => category.Properties).flatten().value()

And then checks that it contains all filters array elements, using _.difference method. 然后使用_.difference方法检查它是否包含所有filters数组元素。

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

another fancy way through _.conforms 通过_.conforms另一种花哨的方法

var res = _.filter(
    products.Products, 
    _.conforms({'Categories': function(categories) {
        return _.chain(categories)
            .flatMap('Properties') // flat arrays 
            .uniq() // remove dublicates
            .keyBy() // transform to objects with Properties keys 
            .at(filters) // get objects values by filters
            .compact() // remove undefineds
            .size() // get size
            .eq(filters.length) // compare to filters size
            .value();
    }
}))

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. 这将适用于要过滤给定属性的项目列表,这些属性是“ doorColour”之类的字符串,还是表示给定属性路径的字符串数组,例如['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);

Eg fLFIISOAGP (contacts, ['person','name'], ['Joh','Pau',Pet']); 例如fLFIISOAGP (联系人,['person','name'],['Joh','Pau',Pet']); with items of structure {contact, business:null, personal:{name:'John'}}. 其结构为{contact,business:null,personal:{name:'John'}}。

For the original question - this will also work - I would use this repeatedly on a list of items to filter with different keys to filter on more than one property. 对于最初的问题-这也将起作用-我将在项目列表上反复使用此选项,以使用不同的键进行过滤,以对多个属性进行过滤。

const firstFilteredResult = filteredListForItemsIncludingSubstringsOnAGivenProperty( products.Products, ["Categories", "0", "Properties"], ["Type 3"]);

const secondFilteredResult = filteredListForItemsIncludingSubstringsOnAGivenProperty( firstFilteredResult, ["Categories", "2", "Properties"], ["Tech 1"]);

expect(secondFilteredResult[0]['Title']).to.equal( "A"); expect(secondFilteredResult[1]['Title']).to.equal( "B"); expect(secondFilteredResult.length).to.equal(2);

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

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