js对象嵌套数组


If you would like to check the implementation of how to filter and sort an array of nested objects only, you can check the below story:

如果您只想检查如何对嵌套对象数组进行过滤和排序的实现,则可以查看以下内容:





Searching through an array of objects and arrays is very similar to the above implementation,but with small changes.

搜索对象数组和数组与上面的实现非常相似,但是变化很小。

Consider the below array of JSON objects and arrays exported from cakes.ts

考虑下面的JSON对象数组和从cakes.ts导出的数组

//cakes.ts
export const cakes=[
{
“id”: “0001”,
“type”: “donut”,
“name”: “Cake”,
“ppu”: 0.56,
“batters”:{
“batter”:[
{ “id”: “1001”, “type”: “Regular” },
{ “id”: “1002”, “type”: “Chocolate” },
{ “id”: “1003”, “type”: “Blueberry” },
{ “id”: “1004”, “type”: “Devil’s Food” }]
},
“topping”:[
{ “id”: “5001”, “type”: “None” },
{ “id”: “5002”, “type”: “Glazed” },
{ “id”: “5005”, “type”: “Sugar” },
{ “id”: “5007”, “type”: “Powdered Sugar” },
{ “id”: “5006”, “type”: “Chocolate with Sprinkles” },
{ “id”: “5003”, “type”: “Chocolate” },
{ “id”: “5004”, “type”: “Maple” }]
},
{
“id”: “0002”,
“type”: “donut”,
“name”: “Raised”,
“ppu”: 0.12,
“batters”:{
“batter”:[
{ “id”: “1001”, “type”: “Strawberry” }]
},
“topping”:[
{ “id”: “5001”, “type”: “Vanilla” },
{ “id”: “5002”, “type”: “Mango” },
{ “id”: “5005”, “type”: “Cherry” },
{ “id”: “5003”, “type”: “Chocolate” },
{ “id”: “5004”, “type”: “Butterscotch” }]
},
{
“id”: “0003”,
“type”: “donut”,
“name”: “Old Fashioned”,
“ppu”: 0.34,
“batters”:{
“batter”:[
{ “id”: “1001”, “type”: “Regular” },
{ “id”: “1002”, “type”: “Vanilla” }
]
},
“topping”:[
{ “id”: “5001”, “type”: “None” },
{ “id”: “5002”, “type”: “Chocolate Chips” },
{ “id”: “5003”, “type”: “Black Currant” },
{ “id”: “5004”, “type”: “Pista” }
]}
]

It is an array of 3 JSON objects. Each object describes the batter and topping for a different type of Cake.

它是3个JSON对象的数组。 每个对象都描述了不同类型蛋糕的面糊和馅料。

Each object contains properties having 4 types of values: String,Number,Object and Array.

每个对象包含具有4种类型的值的属性:字符串,数字,对象和数组。

Template:

模板:

<input type=”text” [(ngModel)]=”searchTerm” name=”searchTerm” placeholder=”Search”><ul><li *ngFor="let x of cakes|filter:searchTerm">
<div>
<span appHighlight [search]="searchTerm">{{x.name}}-</span>
Type:<span appHighlight [search]="searchTerm">{{x.type}}-</span>
PPU:<span appHighlight [search]="searchTerm">{{x.ppu}}</span>
</div><span>Batters</span>
<ul>
<li *ngFor="let y of x.batters.batter">
<span appHighlight [search]="searchTerm" >{{y.id}}:</span>
<span appHighlight [search]="searchTerm">{{y.type}}</span>
</li>
</ul><span>Toppings</span>
<ul>
<li *ngFor="let z of x.topping">
<span appHighlight [search]="searchTerm">{{z.id}}:</span>
<span appHighlight [search]="searchTerm">{{z.type}}</span>
</li>
</ul>
</li>
</ul>

We have constructed a list with the above JSON data.

我们已经使用上述JSON数据构造了一个列表。

appHighlight is the selector for a directive that highlights the search results in the list below.

appHighlight是用于在以下列表中突出显示搜索结果的指令的选择器。

Component Class:

组件类别:

import {cakes} from '../cakes';export class AppComponent {
searchTerm:string=””;
cakes=[...cakes];}

The above class is self-explanatory. We have declared and initialized the ngModel searchTerm and the cakes array.

上面的类是不言自明的。 我们已经声明和初始化的ngModel SEARCHTERM蛋糕阵列。

Now lets check the HighlightDirective.

现在让我们检查HighlightDirective。

@Directive({selector: ‘[appHighlight]’})export class HighlightDirective {@Input() search:any;
constructor(private element:ElementRef,private renderer:Renderer2) {}setBackgroundColor(search){
if(search){
let value=this.element.nativeElement.innerHTML;
let newValue=
isNaN(value)?value.toString().toUpperCase():value.toString();let newSearch=
isNaN(search) ? search.toString().toUpperCase():search.toString();newValue.indexOf(newSearch) > -1 ?this.renderer.setStyle(this.element.nativeElement,’backgroundColor’,’yellow’):this.renderer.setStyle(this.element.nativeElement,’backgroundColor’,’transparent’);
}
else{
this.renderer.setStyle(this.element.nativeElement,’backgroundColor’,’transparent’);
}
}ngOnChanges(change:SimpleChanges){
if(change.search){
this.setBackgroundColor(change.search?.currentValue);
}}
}

The searchTerm value is passed to the Directive via Input binding. Whenever we enter any searchTerm, ngOnChanges hook is triggered, which calls setBackgroundColor().

searchTerm值通过输入绑定传递给指令。 每当我们输入任何searchTerm时,就会触发ngOnChanges挂钩,该挂钩将调用setBackgroundColor()。

In this method,we are checking if the list element content matches the searchTerm or not. If there is a match, we are highlighting the entire element with yellow color.

在这种方法中,我们正在检查列表元素的内容是否与searchTerm相匹配。 如果匹配,我们将用黄色突出显示整个元素。

Lets now check the Filtering functionality.

现在让我们检查过滤功能

import { PipeTransform, Pipe} from ‘@angular/core’;
import { filter } from ‘rxjs/operators’;
import * as _ from ‘lodash/fp’;@Pipe({name:’filter’})export class FilterPipe implements PipeTransform{transform(items,searchTerm){
if(searchTerm){let newSearchTerm=!isNaN(searchTerm)? searchTerm.toString(): searchTerm.toString().toUpperCase();return items.filter(item=>{
return this.lookForNestedObject(item,newSearchTerm);
})
}
else{return items;}
}lookForNestedObject(item,newSearchTerm){
let origItem={...item};
let that=this;
let count=0;parseNestedObject(item);function parseNestedObject(item){
for(let key in item){if(_.isPlainObject(item[key])){
if(origItem[key]) { delete origItem[key]}parseNestedObject(item[key]);
}else if(Array.isArray(item[key])){
if(origItem[key]) { delete origItem[key]}parseNestedObject(item[key]);
}
else{
count++;
if(origItem[key]) { delete origItem[key]}
origItem[key+count]=item[key];
}}
}
return that.search(item,origItem,newSearchTerm);
}search(item,origItem,newSearchTerm){
let filteredList=[];
let prop=””;
for(let koy in origItem){prop=isNaN(origItem[koy]) ? origItem[koy].toString().toUpperCase() : origItem[koy].toString();if(prop.indexOf(newSearchTerm) > -1){
filteredList.push(item);
return filteredList;
}}
}
}

To help us in this task, I have used the lodash package. You can install the package using:

为了帮助我们完成此任务,我使用了lodash软件包。 您可以使用以下方法安装软件包:

npm install — save lodash

Import the package in the FilterPipe Class as:

将包导入到FilterPipe类中为:

import * as _ from ‘lodash/fp’;

The class has defined 3 methods.

该类定义了3种方法。

The transform() needs to be defined because the class implements the PipeTransform Interface. It accepts the cakes array(named as items)and the searchTerm as arguments.

由于该类实现了PipeTransform接口,因此需要定义transform ()。 它接受cakes数组(命名为项目)和searchTerm作为参数。

transform(items,searchTerm){
if(searchTerm){let newSearchTerm=!isNaN(searchTerm)? searchTerm.toString(): searchTerm.toString().toUpperCase();return items.filter(item=>{
return this.lookForNestedObject(item,newSearchTerm);
})
}
else{return items;}
}
  1. Only if the searchTerm has a non-null value, we shall be performing a search, else we shall return the original users list as it is back to the component.
if(searchTerm){
......Search functionality......
}
else{
return items;
}

Now lets check when there is a non-null searchTerm. We have the below logic.

现在让我们检查一下是否有一个非null的searchTerm。 我们有以下逻辑。

let newSearchTerm=!isNaN(searchTerm)? searchTerm.toString(): searchTerm.toString().toUpperCase();return items.filter(item=>{
return this.lookForNestedObject(item,newSearchTerm);
})

2. If the searchTerm is numeric, convert it into a string and if it is non-numeric,convert it into a string and into uppercase.We assign the modified value to a variable newSearchTerm.

2.如果searchTerm是数字,则将其转换为字符串,如果非数字,则将其转换为字符串和大写字母。我们将修改后的值分配给变量newSearchTerm。

3. Next we are applying the filter method to the users array. The argument item of the filter method will be an element of the array. Each item along with the newSearchTerm is passed into the next method lookForNestedObject().

3.接下来,我们将filter方法应用于users数组。 filter方法的参数项将是数组的元素。 每个项目与newSearchTerm一起传递到下一个方法lookForNestedObject ()。

This method could be slightly tricky, where the concept of closures has been used.

在使用闭包的概念时,此方法可能会有些棘手。

lookForNestedObject(item,newSearchTerm){
let origItem={...item};
let that=this;
let count=0;parseNestedObject(item);function parseNestedObject(item){
for(let key in item){if(_.isPlainObject(item[key])){
if(origItem[key]) { delete origItem[key]}parseNestedObject(item[key]);
}else if(Array.isArray(item[key])){
if(origItem[key]) { delete origItem[key]}parseNestedObject(item[key]);
}
else{
count++;
if(origItem[key]) { delete origItem[key]}
origItem[key+count]=item[key];
}}
}
return that.search(item,origItem,newSearchTerm);
}
  1. We are storing a copy of the item object into another block scope variable origItem, because we want to keep the item object intact. Any kind of manipulation will be done to origItem. count will help us later in creating unique properties when restructuring the origItem.
    我们要将 项目对象的副本存储到另一个块范围变量origItem中 ,因为我们希望保持项目对象的完整性。 可以对origItem进行任何类型的操作。 在重组origItem时,count将帮助我们稍后创建唯一属性。
let origItem={…item};
let count=0;

2. Closures revolve around the concept of inner functions. We have defined another function named parseNestedObject() within the lookForNestedObject(). The purpose of this function is to identify all the nested objects and and arrays in the parent object, delete the nested object and array from the parent object but add the nested object’s properties and array values back to the parent object.

2.闭包围绕内部功能的概念。 我们在lookForNestedObject ()中定义了另一个名为parseNestedObject ()的函数 。 此功能的目的是识别父对象中的所有嵌套对象和数组,从父对象中删除嵌套对象和数组,但将嵌套对象的属性和数组值添加回父对象。

This gives us a single object with properties having only string or numeric or date values. There are no properties having objects or arrays as values.

这为我们提供了一个仅具有字符串,数字或日期值的属性的对象。 没有将对象或数组作为值的属性。

parseNestedObject(item);function parseNestedObject(item){
for(let key in item){
if(_.isPlainObject(item[key])){
if(origItem[key]) { delete origItem[key]}parseNestedObject(item[key]);
}
else if(Array.isArray(item[key])){
if(origItem[key]) { delete origItem[key]}
parseNestedObject(item[key]);
}
else{
count++;
if(origItem[key]) { delete origItem[key]}
origItem[key+count]=item[key];
}}
}

The function iterates over the item object and checks if any property contains another object or an array as a value.

该函数遍历item对象并检查是否有任何属性包含另一个对象或数组作为值。

Lodash method _.isPlainObject() helps us identify if the property’s value is a plain object literal or not.

Lodash方法_.isPlainObject()可帮助我们识别属性的值是否为普通对象文字。

Array.isArray() helps us identify if the property’s value is an array or not.

Array.isArray()帮助我们确定属性的值是否为数组。

If its an array or an object, we perform 2 actions:

如果它是数组或对象,我们将执行2个操作:

=>Delete the property from the origItem.

=>从origItem中删除属性

=>Call the parseNestedObject() again passing the property’s value i.e object literal or the array as argument.

=>再次调用parseNestedObject()传递属性值,即对象文字或数组作为参数。

Keep repeating this until we reach a point where no property has object or array as value. Once that point is reached, control goes to the else condition and we again perform 3 actions:

不断重复这一过程,直到到达没有属性将对象或数组作为值的地步。 达到该点后,控制权转到else条件,我们再次执行3个动作:

count++;
if(origItem[key]) { delete origItem[key]}
origItem[key+count]=item[key];

=>Increment the count.

=>增加计数。

=>Delete the property from the origItem.

=>从origItem中删除属性

=>Again add the property and its string/numeric/date value to the origItem. Its important to append the count value to the original property value because the nested objects in each JSON cake object have same property names:id and type.

=>再次将属性及其字符串/数字/日期值添加到origItem中 。 将计数值附加到原始属性值很重要,因为每个JSON Cake对象中的嵌套对象具有相同的属性名称:id和type。

We really dont want the property values to get overwritten.

我们真的不希望属性值被覆盖。

The purpose is to reconstruct the entire object to help us in the search task.

目的是重建整个对象,以帮助我们完成搜索任务。

3. Finally call the search() passing the intact item object, reconstructed origItem object and the newsearchTerm as argument.

3.最后调用传递完整项目对象,重构的origItem对象和newsearchTerm作为参数的search()

search(item,origItem,newSearchTerm){
let filteredList=[];
let prop=””;
for(let koy in origItem){prop=isNaN(origItem[koy]) ? origItem[koy].toString().toUpperCase() : origItem[koy].toString();if(prop.indexOf(newSearchTerm) > -1){
filteredList.push(item);
return filteredList;
}}
}

Here are are iterating through the reconstructed origItem to check if a property’s value is non-numeric. If yes, convert it into string and then uppercase. If not, just convert it into string. We assign the modified property’s value to a block variable prop.

这里是遍历重构的origItem的内容,以检查属性的值是否为非数字。 如果是,请将其转换为字符串,然后大写。 如果没有,只需将其转换为字符串。 我们将修改后的属性的值分配给块变量prop

We are checking if the prop contains a portion or the entire newsearchTerm using the indexOf operator.

我们正在使用indexOf运算符检查道具是否包含部分或整个newsearchTerm

If yes, we add the object item whose property value includes the newsearchTerm to an array filteredList and return it back.

如果是的话,我们增加其属性值包括newsearchTerm到一个数组filteredList目标选项 ,然后返回。



Searching for string “va” 搜索字符串“ va”

Searching for string “sp”

搜索字符串“ sp”

Searching for number 12

搜索数字12

You can check the entire working below:

您可以在下面检查整个工作:

翻译自: https://medium.com/swlh/filtering-an-array-of-nested-arrays-and-objects-using-angular-pipes-611af3b356f0

js对象嵌套数组