【问题标题】:Lodash: filter a nested object by multiple propertiesLodash:通过多个属性过滤嵌套对象
【发布时间】:2016-12-14 10:25:41
【问题描述】:

考虑以下示例:

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

但我不知道如何组合属性以进行组合搜索。

【问题讨论】:

  • 您的过滤器总是与Properties相关?
  • 是的,总是这样。

标签: javascript json lodash


【解决方案1】:

使用_.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>

【讨论】:

  • 过滤器中的所有属性都必须在产品上。因此,“类型 3 出现在 C 中”但“技术 1”没有。在我的示例中,产品 A + B 应该被退回,因为它们都有“Type 3”和“Tech 1”。
【解决方案2】:

如果我正确理解您的问题,此代码可能会有所帮助:

_.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()

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

希望对你有帮助。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      最后一个参数 '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);
      

      例如fLFIISOAGP(联系人,['person','name'], ['Joh','Pau',Pet']); 具有结构 {contact, business:null, personal:{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);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-23
        • 2017-01-10
        • 2020-04-27
        • 2018-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多