基于javascript中深层嵌套对象中的值过滤数组

时间:2017-02-10 19:49:30

标签: javascript arrays json lodash

我有以下结构的数组:

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

我需要根据指标数组中name属性的值获取过滤数组,删除不匹配的指标以及带有空指标的topic和sub_categories。因此,对于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方法,但所有示例在所有级别(即子级)上只有一级嵌套或相同的键。无论是回到新阵列还是改变现有阵列,我都没问题。

6 个答案:

答案 0 :(得分:4)

以下是基于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; }




答案 1 :(得分:4)

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

function deepFilter(array, indicator) {
    return array.filter(function iter(o) {                
        return Object.keys(o).some(function (k) {
            if (typeof o[k] === 'string' && o[k].indexOf(indicator) !== -1) {
                return true;
            }
            if (Array.isArray(o[k])) {
                o[k] = o[k].filter(iter);
                return o[k].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 }] }] }];

console.log(deepFilter(topics, 'foo'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 2 :(得分:3)

这几乎可以用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代替过滤器:

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需要为旧浏览器进行填充(尽管很简单)。

答案 3 :(得分:1)

这也将修改现有的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);




答案 4 :(得分:0)

还有一个实现。

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

答案 5 :(得分:0)

您可以使用deepdash的{​​{1}}扩展名中的_.filterDeep

lodash

这里是您的情况的full test