JavaScript嵌套数组过滤问题

时间:2018-12-25 17:21:46

标签: javascript arrays reactjs filter

给出一个表示JSON的JavaScript对象-

[
    {
        "Id": "8868dfdd-9b4e-4bad-a4ce-ecae6a3cc828",
        "Name": "Company 1",
        "Locations": [
            {
                "Id": "bd017b9c-b62e-43aa-9f00-c164a855eed1",
                "Name": "Location 1",
                "Departments": [
                    {
                        "Id": "c9e4afe3-bbdb-474f-9062-2935025bfa2e",
                        "Name": "Department 1",
                        "Employees": [
                            {
                                "Id": "92c3a085-5712-422d-8b0f-922b57889c4f",
                                "Name": "Employee 1",
                                "Title": "FrontEnd Engineer",
                                "Location": "New York",
                                "Photo": ""
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

鉴于可能存在多个公司,地点和部门,我想按员工姓名过滤此数据结构。这是我的尝试,但由于我对Array.filterArray.reduce的工作方式的了解,显然它不起作用。

filterContacts = search => {
    if (search.trim() === "") {
        this.setState({ filteredContacts: null, search: search });
    } else {
        let filteredArray = this.state.contacts.reduce((f, c) => {
            let clone = [];
            for (let i = 0; i < c.Locations.length; i++) {
                const l = c.Locations[i];
                for (let j = 0; j < l.Departments.length; j++) {
                    const d = l.Departments[j];
                    for (let k = 0; k < d.Employees.length; k++) {
                        const e = d.Employees[k];
                        if (e.Name.search(new RegExp(search, "i") > -1)) {
                            clone.push(l);
                        }
                    }
                }
            }
            return clone;
        }, []);
        this.setState({ filteredContacts: filteredArray, search: search });
    }
};

任何帮助将不胜感激。谢谢。

2 个答案:

答案 0 :(得分:2)

使用时:

 let clone = [];

reduce()回调的顶部,您丢弃了累加器-不断在循环中传递的数组,该循环在代码中以f的形式传递。您应该每次都使用相同的reduce累加器并将其推入。最后,您将得到一个包含所有值的数组:

let arr = [{"Id": "8868dfdd-9b4e-4bad-a4ce-ecae6a3cc828","Name": "Company 1","Locations": [{"Id": "bd017b9c-b62e-43aa-9f00-c164a855eed1","Name": "Location 1","Departments": [{"Id": "c9e4afe3-bbdb-474f-9062-2935025bfa2e","Name": "Department 1","Employees": [{"Id": "92c3a085-5712-422d-8b0f-922b57889c4f","Name": "Employee 1","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}]}]}]}]

let emp = arr.reduce((f, obj) => {
  obj.Locations.forEach(location => 
    location.Departments.forEach(department => 
      f.push(...department.Employees.filter(emp => emp.Name == "Employee 1"))
    )
  )
  return f
}, []) // <-- this array will get passed to every loop as `f`

console.log(emp)

根据评论进行编辑

如果您要坚持下去的结构,您可以根据它们下面的已过滤数组的长度来过滤数组。这是一个带有一些额外数据的示例,请参见过滤工作,第一个完全过滤,第三个有两个具有相同名称的员工。基本上,它将保留具有其部门与雇员匹配的部门的任何项目:

let arr = [
  {"Id": "someother","Name": "Company 2","Locations": [{"Id": "loc2Id","Name": "Location 2","Departments": [{"Id": "d2","Name": "Department 2","Employees": [{"Id": "emp","Name": "Employee 2","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}]}]}]}, 
  {"Id": "8868dfdd-9b4e-4bad-a4ce-ecae6a3cc828","Name": "Company 1","Locations": [{"Id": "bd017b9c-b62e-43aa-9f00-c164a855eed1","Name": "Location 1","Departments": [{"Id": "c9e4afe3-bbdb-474f-9062-2935025bfa2e","Name": "Department 1","Employees": [{"Id": "92c3a085-5712-422d-8b0f-922b57889c4f","Name": "Employee 1","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}]}]}]},
  {"Id": "someother","Name": "Company 2","Locations": [{"Id": "loc2Id","Name": "Location 2","Departments": [{"Id": "d2","Name": "Department 2","Employees": [{"Id": "emp","Name": "Employee 1","Title": "FrontEnd Engineer","Location": "New York","Photo": ""}, {"Id": "emp","Name": "Employee 1","Title": "FrontEnd Engineer 2","Location": "New York","Photo": ""}]}]}]}, 
]


let f = []
let emp = arr.filter(arr => 
  arr.Locations.filter(location => 
    location.Departments.filter(department => {
      let emp = department.Employees.filter(emp => emp.Name == "Employee 1")
      return emp.length ? emp: false 
    }
    ).length
  ).length
) // <-- this array will get passed to every loop as `f`

console.log(emp)

答案 1 :(得分:1)

这是另一个使用map的简短版本:

var rx=new RegExp(search,'i'),emp=[];
obj.map(c=>
 c.Locations.map(l=>
 l.Departments.map(d=>
 d.Employees.map(e=>
  {if(e.Name.match(rx)) emp.push(e)}
))));

search包含不区分大小写的搜索模式。结果是emp,这是一组雇员对象。

如上所述,map并不是真正必要的,可以用forEach代替,但是在我看来,它更容易编写并且不会真正导致更多的开销。

编辑,这次使用reduce()

这是圣诞节,我手上的时间太多了,所以我一直在玩。以下解决方案将筛选出所寻找的雇员,而不会显示其不匹配的同事,并使原始阵列保持不变:

const rd=(prop,fun)=>
             (a,el)=>{
var arr=el[prop].reduce(fun,[]);
if(arr.length){
  var r=Object.assign({},el);
// alternatively: use spread operator
// var r={...el};
  r[prop]=arr;a.push(r);}
return a;}

var rx=new RegExp('employee 1','i');

 var f=ma.reduce(
  rd('Locations',
  rd('Departments',
  rd('Employees',(a,e)=>{
     if(e.Name.match(rx))
      a.push(e);
     return a;}
,[]),[]),[]),[]);

f将包含一个数组,其中仅包含员工将匹配正则表达式rx的那些位置,部门和员工。

rd()是一个生成器函数,返回在三个不同的reduce级别上使用的实际过滤器函数。

静态Object.assign()函数是一种生成浅对象副本的简便方法(类似于数组的slice()方法)。