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

时间:2016-12-14 10:25:42

标签: javascript json lodash

考虑以下示例:

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'];

使用这些过滤器,我想返回产品A和产品B.

我目前有这个:

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

但我仍然坚持如何将这些属性组合起来进行组合搜索。

4 个答案:

答案 0 :(得分:2)

使用_.filter()来迭代产品。对于每个产品,使用_.flatMap()合并属性列表,并使用_.intersection()_.size()查找类别中存在的过滤器数量。将其与原始过滤器数量进行比较,并返回比较的响应。

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>

答案 1 :(得分:0)

如果我理解你的问题,这段代码可能会有所帮助:

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

它计算每种产品的所有属性的并集:

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

然后使用filters方法检查它是否包含所有_.difference数组元素。

希望它有所帮助。

答案 2 :(得分:0)

通过_.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();
    }
}))

答案 3 :(得分:0)

这将适用于要过滤给定属性的项目列表,该属性是“ doorColour”之类的字符串,还是表示给定属性路径的字符串数组,例如[“ town”,“ street”,“ doorColour” ']表示嵌套在项上的值为town.street.doorColour。

它还可以过滤多个值,因此您只需要传入表示要保留的字符串值的子字符串数组即可,它将保留具有字符串值的项目,该字符串值包含在substrings数组中的任何子字符串。

如果将其设置为false,则最终参数'includes'可确保保留这些值,它将排除这些值并保留不具有您在子字符串数组中指定的任何值的

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

例如 fLFIISOAGP (联系人,['person','name'],['Joh','Pau',Pet']); 的结构项为{联系人,业务:空,个人:{name:'John'}}。

对于最初的问题-这也将起作用-我将在一系列项目中反复使用此代码,以使用不同的键进行过滤,以对多个属性进行过滤。

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

相关问题