前端操作数据-处理后端数据整理

1,314 阅读10分钟

一、for-in 和 for-of 的区别

for-in循环主要用于遍历对象,for()中的格式:for(keys in zhangsan){}

keys表示obj对象的每一个键值对的键!!所有循环中,需要使用obj[keys]来取到每一个值!!!for-in 循环,遍历时不仅能读取对象自身上面的成员属性,也能延续原型链遍历出对象的原型属性所以,可以使用hasOwnProperty判断一个属性是不是对象自身上的属性obj.hasOwnProperty(keys)==true 表示这个属性是对象的成员属性,而不是原先属性。

//声明一个Peson类
function Person(){
    this.name = "张三";
    this.age = 14;
    this.func1 = function(){

    }
}
//实例化这个类
var zhangsan = new Person();
//使用for-in遍历这个对象
for(keys in zhangsan){
    console.log(zhangsan[keys])
}

for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串

1. 数组

  var arr = ['a', 'b', 'c', 'd'];
  for (let a in arr) {
    console.log(a); // 0 1 2 3
  }
  for (let a of arr) {
     console.log(a); // a b c d
  }
 12345678910

上面代码表明,for…in循环读取键名,for…of循环读取键值。如果要通过for…of循环,获取数组的索引,可以借助数组实例的entries方法和keys方法

2.Set 和 Map 结构

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}
// Gecko  Trident    Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-26212345678910111213141516

上面代码演示了如何遍历 Set 结构和 Map 结构。值得注意的地方有两个,首先,遍历的顺序是按照各个成员被添加进数据结构的顺序。其次,Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。

3.类似数组的对象
类似数组的对象包括好几类。下面是for…of循环用于字符串、DOM NodeList 对象、arguments对象的例子。

// 字符串
  var str = "hello";
  for (let s of str) {
    console.log(s); // h e l l o
  }  
// DOM NodeList对象
  let paras = document.querySelectorAll("p");
  for (let p of paras) {
   p.classList.add("test");
  }  
// arguments对象
  function printArgs() {
    for (let x of arguments) {
      console.log(x);
    }
  }
printArgs('a', 'b');// 'a' 'b'1234567891011121314151617

与其他遍历语法的比较

forin循环有几个缺点  
①数组的键名是数字,但是forin循环是以字符串作为键名“0”、“1”、“2”等等。 
②forin循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。  
③某些情况下,forin循环会以任意顺序遍历键名。  
forin循环主要是为遍历对象而设计的,不适用于遍历数组。for…of循环  
有着同forin一样的简洁语法,但是没有forin那些缺点。  
不同于forEach方法,它可以与breakcontinuereturn配合使用。  
提供了遍历所有数据结构的统一操作接口

二、forEach()、map()、every()、some()和filter()的用法

1.forEach(),用于遍历数组,无返回值

这里先给出一个数组(以下例子通用):

var arr = [1,-2,3,4,-5];

然后我要做事情的就是,将数组中的每一项翻倍。

arr.forEach(function(item,index,array){
    array[index] = item * 2;
});
console.log(arr);   // [2,-4,6,8,-10]

可以看到,forEach()可以传入一个匿名函数作为参数,而该匿名函数有含有三个参数,其依次代表数组遍历时的当前元素item,数组遍历时的当前元素的索引index,以及正在遍历的数组array。有了这三个参数,可以方便我们做很多事情,比如说示例当中将每一项数组元素翻倍,这时需要用到第一个参数item。但是,仅仅只是将item乘以2可不行,我们还得将其赋值给原来的数组,这时我们就得用到后面两个参数index和array。

根据上述可知,array[index]是全等于item的。

arr.forEach(function(item,index,array){
    console.log(array[index] === item);   // true
});

2.map(),用于遍历数组,返回处理之后的新数组

var newArr = arr.map(function(item,index,array){
    return item * 2;
});
console.log(newArr);   // [2,-4,6,8,-10]

可以看到,该方法与forEach()的功能类似,只不过map()具有返回值,会返回一个新的数组,这样处理数组后也不会影响到原有数组。

3.every(),用于判断数组中的每一项元素是否都满足条件,返回一个布尔值

var isEvery = arr.every(function(item,index,array){
    return item > 0;
});
console.log(isEvery);   // false

可以看到,示例中是要判断数组arr中的元素是否都为正数,很显然不是,所以该方法最终返回false。

4.some(),用于判断数组中的是否存在满足条件的元素,返回一个布尔值

var isSome = arr.some(function(item,index,array){
    return item < 0;
});
console.log(isSome);   // true

可以看到,该方法与every()类似,示例中是要判断数组arr中是否存在负数元素,很显然存在,所以该方法最终返回true。

5.filter(),用于筛选数组中满足条件的元素,返回一个筛选后的新数组

var minus = arr.filter(function(item,index,array){
    return item < 0;
});
console.log(minus);   // [-2, -5]

可以看到,示例中是要筛选出数组arr中的所有负数,所以该方法最终返回一个筛选后的新数组[-2, -5]。

补充: 以上五大方法除了传递一个匿名函数作为参数之外,还可以传第二个参数,该参数用于指定匿名函数内的this指向,例如:

// 只传一个匿名函数
arr.forEach(function(item,index,array){
    console.log(this);  // window
});


// 传两个参数
arr.forEach(function(item,index,array){
    console.log(this);  // [1, -2, 3, 4, -5]
},arr); 

三、判断和查找

  • includes()indexOf 是用元素对参数精确匹配 === 进行查找

  • find()findIndex() 是传入比较行为(函数),按指定方式查找

    console.log([1, 2, 3, 4, 5].includes(3));

    const a = { v: 3 }; const array = [ { v: 1 }, { v: 2 }, { v: 3 }, { v: 4 }, ]; console.log(array.includes(a));

    const arr = [ { v: 1} { v: 2} ];

    const a = { v: 2 }

    // console.log(!!);

    count f = array.find(it => it.v === a.v) // 此时 f !== a

    console.log(array.find(it => it.v === a.v)); console.log(array.findIndex(it => it.v === a.v) >= 0);

    console.log(!![0, 1, 2, 3].find(it => it === 0));

四、获取数组对象中某个属性的最大值或最小值

 数组对象如下,求id的最大值和最小值

list: [
        { id: 1, name: 'jack' },
        { id: 2, name: 'may' },
        { id: 3, name: 'shawn' },
        { id: 4, name: 'tony' },
      ]

1、Math方法

// 最大值 4
Math.max.apply(Math,this.list.map(item => { return item.id }))
 
// 最小值 1
Math.min.apply(Math,this.list.map(item => { return item.id }))

2、sort排序

需要注意的是,sort()对数组排序,不开辟新的内存,对原有数组元素进行调换, 所以这种操作会使得原来的数组元素的位置发生变化

// 最大值 4
this.list.sort((a, b) => { return b-a })[0].id

// 最小值 1this.list.sort((a, b) => { return a-b })[0].id 

五、向一个对象数组里面添加新的属性

向一个对象数组里面添加新的属性

var arry= [{a:11,b:22,c:33,d:44},{a:11,b:0,c:0,d:44},{a:11,b:22,c:99,d:99}];
var arry2=[];
arry.map(((item, index)=> {    
  arry2.push(Object.assign({},item,{mess1:item.c,mess2:item.d}))
}))
console.log(arry2);

将一个对象数组数据拿出来变成另一个对象

var arry= [{a:11,b:22,c:33,d:44},{a:11,b:0,c:0,d:44},{a:11,b:22,c:99,d:99}];
var arry2=[];
arry.map(((item, index)=> {   
  arry2.push(Object.assign({},{mess1:item.c,mess2:item.d}))
))
console.log(arry2);

六、树形数据处理(经典实际运用)

一.数组对象不匹配

后端返回数据格式:

data: [
  {
    id: '003268955',
    name: 'tom',
    age: 18
  },
  {
    id: '0335689754',
    name: 'mark',
    age: 23
  }
];

  • 假设:
  1. 这里的id返回的类型是string,而你需要的是number类型

    data = data.map(res => { return { ...res, id: Number(res.id) } }) //输出=> [ { id: 3268955, name: 'tom', age: 18 }, { id: 335689754, name: 'mark', age: 23 } ];

2.后端返回的是name字段名,而你需要的是username(这里我们直接复制出一个新的key就行,旧的key值可以保留也可删除)

//不删除旧key
data = data.map(res => {
    return {
        ...res,
        username: res.name
    }
})
//输出=>
[
  { id: '003268955', name: 'tom', age: 18, username: 'tom' },
  { id: '0335689754', name: 'mark', age: 23, username: 'mark' }
];

//删除旧key
data = data.map(res => {
   let username = res.name
   delete res.name
    return {
        ...res,
        username: username
    }
})
//输出=>
[
  { id: '003268955', age: 18, username: 'tom' },
  { id: '0335689754', age: 23, username: 'mark' }
];

3.checkbox情况,你还需要一个变量checked来处理有没有被选择的情况(初始值置为false)

data = data.map(res => {
   let username = res.name
   delete res.name
    return {
        ...res,
        checked: false
    }
})
//输出=>
[
  { id: '003268955', age: 18, checked: false },
  { id: '0335689754', age: 23, checked: false }
];

二、树状数据结构

后端返回数据:

[
  {
    title: '一号楼',
    key: '100',
    children: [
      {
        title: '一单元',
        key: '1001',
        children: [
          { title: '3405', key: '10010' },
          { title: '3302', key: '10011' }
        ]
      }
    ]
  }
];
  • 假设

1.使用的树插件的key以及value字段名称是id和name;(递归方式进行替换并删除旧key)

function format(data){
  data.forEach(res=>{
    res.id = res.key;
    res.name = res.title;
    delete res.key;
    delete res.title;
    if(res.children){
      format(res.children)
    }
  })
}
format(data)

//输出==>

[  {    children: [      {        children: [          { id: '10010', name: '3405' },          { id: '10011', name: '3302' }        ],
        id: '1001',
        name: '一单元'
      }
    ],
    id: '100',
    name: '一号楼'
  }
];

2.希望在最后一个节点显示前面父集的集合:一号楼一单元10010

function format(data,text){
  data.forEach(res=>{
    if(!res.children){
      res.title = text + res.title
    }
    
    if(res.children){
      text += res.title;
      format(res.children,text)
    }
  })
}
format(data,'');//初始text置为空

//输出==>
[  {    title: '一号楼',    key: '100',    children: [      {        title: '一单元',        key: '1001',        children: [          { title: '一号楼一单元3405', key: '10010' },          { title: '一号楼一单元3302', key: '10011' }        ]
      }
    ]
  }
];

3.将节点进行排序

const compare = p => (m, n) => m[p] - n[p];

function format(data, key) {//key:需要排序的字段
  data.sort(compare(key));
  data.forEach(res => {
    if (!res.children) {
      return;
    } else {
      format(res.children, key);
    }
  });
}

format(data, 'title');
//输出=>
[  {    title: '一号楼',    key: '100',    children: [      {        title: '一单元',        key: '1001',        children: [          { title: '3302', key: '10011' },          { title: '3405', key: '10010' }        ]
      }
    ]
  }
];

七、无敌的reduce()

reduce()方法可以搞定的东西,for循环,或者forEach方法有时候也可以搞定,那为啥要用reduce()?这个问题,之前我也想过,要说原因还真找不到,唯一能找到的是:通往成功的道路有很多,但是总有一条路是最捷径的,亦或许reduce()逼格更高...

1、语法

arr.reduce(callback,[initialValue])

reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。

callback (执行数组中每个值的函数,包含四个参数)

    1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
    2、currentValue (数组中当前被处理的元素)
    3、index (当前元素在数组中的索引)
    4array (调用 reduce 的数组)

initialValue (作为第一次调用 callback 的第一个参数。)

2、实例解析 initialValue 参数

先看第一个例子:

var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
})
console.log(arr, sum);

打印结果:
1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10

这里可以看出,上面的例子index是从1开始的,第一次的prev的值是数组的第一个值。数组长度是4,但是reduce函数循环3次。

再看第二个例子:

var  arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
},0) //注意这里设置了初始值
console.log(arr, sum);

打印结果:
0 1 0
1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10

这个例子index是从0开始的,第一次的prev的值是我们设置的初始值0,数组长度是4,reduce函数循环4次。

结论:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。

注意:如果这个数组为空,运用reduce是什么情况?

var  arr = [];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
})
//报错,"TypeError: Reduce of empty array with no initial value"

但是要是我们设置了初始值就不会报错,如下:

var  arr = [];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
},0)
console.log(arr, sum); // [] 0

所以一般来说我们提供初始值通常更安全

3、reduce的简单用法

当然最简单的就是我们常用的数组求和,求乘积了。

var  arr = [1, 2, 3, 4];
var sum = arr.reduce((x,y)=>x+y)
var mul = arr.reduce((x,y)=>x*y)
console.log( sum ); //求和,10
console.log( mul ); //求乘积,24

4、reduce的高级用法

(1)计算数组中每个元素出现的次数

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

let nameNum = names.reduce((pre,cur)=>{
  if(cur in pre){
    pre[cur]++
  }else{
    pre[cur] = 1 
  }
  return pre
},{})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

(2)数组去重

let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
    if(!pre.includes(cur)){
      return pre.concat(cur)
    }else{
      return pre
    }
},[])
console.log(newArr);// [1, 2, 3, 4]

(3)将二维数组转化为一维

let arr = [[0, 1], [2, 3], [4, 5]]
let newArr = arr.reduce((pre,cur)=>{
    return pre.concat(cur)
},[])
console.log(newArr); // [0, 1, 2, 3, 4, 5]

(3)将多维数组转化为一维

let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr = function(arr){
   return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
}
console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]

(4)、对象里的属性求和

var result = [
    {
        subject: 'math',
        score: 10
    },
    {
        subject: 'chinese',
        score: 20
    },
    {
        subject: 'english',
        score: 30
    }
];

var sum = result.reduce(function(prev, cur) {
    return cur.score + prev;
}, 0);
console.log(sum) //60