繁体   English   中英

根据javascript中深度嵌套对象中的值过滤数组

[英]Filtering array based on value in deeply nested object in javascript

我有以下结构的数组:

var topics = [
  {
    "id": 1,
    "name": "topic title 1",
    "sub_categories": [
      {
        "id": 1,
        "name": "category title 1",
        "indicators": [
          {
            "id": 1,
            "name": "indicator 1",
            "sub_category_id": 1
          },
          {
            "id": 7,
            "name": "indicator 7 - foo",
            "sub_category_id": 1
          }
        ]
      },
      {
        "id": 6,
        "name": "category title 6",
        "indicators": [
          {
            "id": 8,
            "name": "indicator 8",
            "sub_category_id": 6
          }
        ]
      }
    ]
  },
  {
    "id": 2,
    "name": "topic title 2",
    "sub_categories": [
      {
        "id": 2,
        "name": "category 2",
        "indicators": [
          {
            "id": 2,
            "name": "indicator 2 - foo",
            "sub_category_id": 2
          }
        ]
      },
      {
        "id": 4,
        "name": "category 4",
        "indicators": [
          {
            "id": 5,
            "name": "indicator 5",
            "sub_category_id": 4
          }
        ]
      }
    ]
  }
];

我需要根据指标数组中名称属性的值获取过滤数组,删除不匹配的指标以及带有空指标的主题和子类别。 所以对于foo输入,结果将是:

var topics = [
  {
    "id": 1,
    "name": "topic title 1",
    "sub_categories": [
      {
        "id": 1,
        "name": "category title 1",
        "indicators": [
          {
            "id": 7,
            "name": "indicator 7 - foo",
            "sub_category_id": 1
          }
        ]
      }
    ]
  },
  {
    "id": 2,
    "name": "topic title 2",
    "sub_categories": [
      {
        "id": 2,
        "name": "category 2",
        "indicators": [
          {
            "id": 2,
            "name": "indicator 2 - foo",
            "sub_category_id": 2
          }
        ]
      }
    ]
  }
];

我尝试使用基于其他类似 SO 问题的 lodash 方法,但所有示例要么只有一层嵌套,要么在所有级别(即子项)上都有相同的键。 无论是取回新数组还是改变现有数组,我都可以。

这是一个基于reducefilterObject.assign的 ES6 解决方案:

 function filterTree(topics, find) { return topics.reduce(function (acc, topic) { const sub_categories = topic.sub_categories.reduce(function (acc, cat) { const indicators = cat.indicators.filter( ind => ind.name.includes(find) ); return !indicators.length ? acc : acc.concat(Object.assign({}, cat, { indicators })); }, []); return !sub_categories.length ? acc : acc.concat(Object.assign({}, topic, { sub_categories })); }, []); } // sample data const topics = [ { "id": 1, "name": "topic title 1", "sub_categories": [ { "id": 1, "name": "category title 1", "indicators": [ { "id": 1, "name": "indicator 1", "sub_category_id": 1 }, { "id": 7, "name": "indicator 7 - foo", "sub_category_id": 1 } ] }, { "id": 6, "name": "category title 6", "indicators": [ { "id": 8, "name": "indicator 8", "sub_category_id": 6 } ] } ] }, { "id": 2, "name": "topic title 2", "sub_categories": [ { "id": 2, "name": "category 2", "indicators": [ { "id": 2, "name": "indicator 2 - foo", "sub_category_id": 2 } ] }, { "id": 4, "name": "category 4", "indicators": [ { "id": 5, "name": "indicator 5", "sub_category_id": 4 } ] } ] } ]; // Call the function var res = filterTree(topics, 'foo'); // Output result console.log(res);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

您可以使用迭代和递归方法来过滤给定的数组,而无需硬连线属性。

 const deepFilter = (array, indicator) => { return array.filter(function iter(o) { return Object.keys(o).some(k => { if (typeof o[k] === 'string' && o[k].includes(indicator)) { return true; } if (Array.isArray(o[k])) { o[k] = o[k].filter(iter); return o[k].length; } }); }); } const topics = [{ id: 1, name: "topic title 1", sub_categories: [{ id: 1, name: "category title 1", indicators: [{ id: 1, name: "indicator 1", sub_category_id: 1 }, { id: 7, name: "indicator 7 - foo", sub_category_id: 1 }] }, { id: 6, name: "category title 6", indicators: [{ id: 8, name: "indicator 8", sub_category_id: 6 }] }] }, { id: 2, name: "topic title 2", sub_categories: [{ id: 2, name: "category 2", indicators: [{ id: 2, name: "indicator 2 - foo", sub_category_id: 2 }] }, { id: 4, name: "category 4", indicators: [{ id: 5, name: "indicator 5", sub_category_id: 4 }] }] }]; console.log(deepFilter(topics, 'foo'));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

这几乎可以通过 ES 5 数组方法完成(IE 9+ 不需要库或 polyfill):

var passed = topics.filter(function(x) {
  return x.subcategories.some(function(y) {
    return y.indicators.some(function(z) {
      return Boolean(z.name.match(/foo/));
    });
  });
});

虽然这完全是一次性代码,但对于易于理解的通用解决方案来说,情况可能太复杂了(尽管我很想看到有人证明我错了)。

更新

在仔细查看输出后,您将需要使用reduce而不是 filter:

var passed = topics.reduce((acc, x) => {
  var hasfoo = x.subcategories.reduce((accum, y) => {
    var ls = y.indicators.filter(z => z.name.match(/foo/));
    if (ls.length) {
      accum.push(Object.assign({}, y, {indicators: ls}));
    }
    return accum;
  }, []);

  if (hasfoo.length) {
    acc.push(Object.assign({}, x, {subcategories: hasfoo}));
  }

  return acc;
}, []);

精明的读者会注意到这里的递归模式。 抽象出来作为练习,我被挖掘出来了。 Object.assign需要为旧浏览器填充(虽然很简单)。

这也将修改现有topics

var result = topics.filter(top => 
    (top.sub_categories = top.sub_categories.filter(cat => 
        (cat.indicators = cat.indicators.filter(i => i.name.match(/foo/))).length)
    ).length
);

例子

 var topics = [{ "id": 1, "name": "topic title 1", "sub_categories": [{ "id": 1, "name": "category title 1", "indicators": [{ "id": 1, "name": "indicator 1", "sub_category_id": 1 }, { "id": 7, "name": "indicator 7 - foo", "sub_category_id": 1 }] }, { "id": 6, "name": "category title 6", "indicators": [{ "id": 8, "name": "indicator 8", "sub_category_id": 6 }] }] }, { "id": 2, "name": "topic title 2", "sub_categories": [{ "id": 2, "name": "category 2", "indicators": [{ "id": 2, "name": "indicator 2 - foo", "sub_category_id": 2 }] }, { "id": 4, "name": "category 4", "indicators": [{ "id": 5, "name": "indicator 5", "sub_category_id": 4 }] }] }]; var result = topics.filter(top => (top.sub_categories = top.sub_categories.filter(cat => (cat.indicators = cat.indicators.filter(i => i.name.match(/foo/))).length)).length); console.log(result);

再一个实现。

        topics.forEach(function(topic, indexTopic, indexTopicArray) { 
                topic.sub_categories.forEach(function(subCat, indexsubCat, arraysubCat) { 
                         subCat.indicators = subCat.indicators.filter(indic => indic.name.includes("foo"));    
                         if(subCat.indicators.length === 0) { 
                                indexTopicArray[indexTopic].sub_categories.splice(indexsubCat, 1); 
    }})});
    console.log(topics);

完整代码。

 var topics = [ { "id": 1, "name": "topic title 1", "sub_categories": [ { "id": 1, "name": "category title 1", "indicators": [ { "id": 1, "name": "indicator 1", "sub_category_id": 1 }, { "id": 7, "name": "indicator 7 - foo", "sub_category_id": 1 } ] }, { "id": 6, "name": "category title 6", "indicators": [ { "id": 8, "name": "indicator 8", "sub_category_id": 6 } ] } ] }, { "id": 2, "name": "topic title 2", "sub_categories": [ { "id": 2, "name": "category 2", "indicators": [ { "id": 2, "name": "indicator 2 - foo", "sub_category_id": 2 } ] }, { "id": 4, "name": "category 4", "indicators": [ { "id": 5, "name": "indicator 5", "sub_category_id": 4 } ] } ] } ]; topics.forEach(function(topic, indexTopic, indexTopicArray) { topic.sub_categories.forEach(function(subCat, indexsubCat, arraysubCat) { subCat.indicators = subCat.indicators.filter(indic => indic.name.includes("foo")); if(subCat.indicators.length === 0) { indexTopicArray[indexTopic].sub_categories.splice(indexsubCat, 1); }})}); console.log(topics);

您可以使用lodashdeepdash扩展中的lodashlodash

var endsWith = 'foo';
var foundFoo = _.filterDeep(
  obj,
  function(value, key) {
    return _.endsWith(value.name, endsWith);
  },
  { tree: { children: ['sub_categories', 'indicators'] } }
);

这是对您的案例的完整测试

暂无
暂无

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

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