数据处理

复杂数据

  • 实际工作中经常会收到后端各种奇葩的数据格式,对应复杂的数据需要进行有效消息的提炼,就需要对JSON数据进行处理,比如如下格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let jsonStr = {
Code: 10000,
Message: "成功",
Time: "2018-11-01 11:09:08",
Result: {
rData: [
{
count: 20,
title: "V1",
color: "#2dc6c8",
},
{
count: 15,
title: "V2",
color: "#b6a2dd",
},
{
count: 30,
title: "V3",
color: "#5ab1ee",
},
{
count: 15,
title: "V4",
color: "#d7797f",
},
{
count: 20,
title: "V5",
color: "#FFB800",
},
],
},
};
1
2
3
4
5
6
7
8
// 转成
let result = [
[20, "#2dc6c8", "V1"],
[15, "#b6a2dd", "V2"],
[30, "#5ab1ee", "V3"],
[15, "#d7797f", "V4"],
[20, "#FFB800", "V5"]
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1.在异步调用成功后,将返回的串转为对象
let obj = JSON.parse(JSON.stringify(jsonStr)) /*将json串转化为model对象*/
console.log(obj)

// 2. 然后取rData的值
let arrObj = obj.Result.rData
console.log(arrObj)

// 3. 遍历arrObj对象中的每个子集对象
let arr = []
for(let i in arrObj) {
var tempArr = []
// 将每个小子集加到临时数组中
tempArr.push(arrObj[i].count,arrObj[i].color,arrObj[i].title)
// 完成后加到大数组
arr.push(tempArr)
}
console.log(arr)
// 另外赋常用操作 将对象直接转数组,仅限于去掉第一层
var array = Array.from(jsonStr)
console.log(array)

深层数据

  • 有时候会获取对象深层次的属性值,比如如下数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 后端接口返回数据
let res = {
code: 200,
msg: "OK",
data: {
config: {
style: {
color: "#f00",
},
},
dataConfig: {
x: "100",
y: "200",
},
},
};
1
2
3
4
// 可以多层判断获取
console.log(res.data && res.data.config && res.data.config.style && res.data.config.style.color)
// 获取使用ES语法,需要考虑兼容性问题
console.log(res.data?.config?.style?.color)
  • 上述方法存在不安全及兼容性问题,可以自定义一个更好的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
const safeGet = (obj, path) => {
// 转成数组
const keys = path.split(".");
for (const key of keys) {
if (obj) {
obj = obj[key];
}
}
return obj;
};
let data = safeGet(res, "data.config.style.color");

console.log(data); // #foo

类型转换

  • 如果有一串后端的数据是JSON对象,需要转成数组形式拿取数据,比如如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let result = {
beijin: 100,
shanghai: 60,
shenzhen: 80,
guangzhou: 90,
};

// 需要转成如下数据
// let Really = [
// { name: beijin, value: 100 },
// { name: shanghai, value: 60 },
// { name: shenzhen, value: 80 },
// { name: guangzhou, value: 90 },
// ];
  • 我们可以转成数组,并为键自定义名称
1
2
3
4
5
6
7
8
9
10
const objTArr = (data, realName, realValue) => {
const res = Object.keys(data).map((key) => {
return { [realName]: key, [realValue]: data[key] };
});
return res;
};

let data = objTArr(result, "name", "value");

console.log(data);

数据分组

  • 如果有两个数组对象,需要合并相关内容进行分组,比如如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const gradeList = [
{ grade1: "一年级" },
{ grade2: "二年级" },
{ grade3: "三年级" },
];
const studentsList = [
{ grade1: ["小明", "小张", "小赵"] },
{ grade2: ["张三", "李四", "王五"] },
{ grade3: ["Tom", "Bob", "John"] },
];

// 处理成如下数据
const studen = [{ name: "小明", grade: "一年级" }];
const gradeList = [
{ grade1: "一年级" },
{ grade2: "二年级" },
{ grade3: "三年级" },
];
const studentsList = [
{ grade1: ["小明", "小张", "小赵"] },
{ grade2: ["张三", "李四", "王五"] },
{ grade3: ["Tom", "Bob", "John"] },
];

// 处理成如下数据
const student = [{ name: "小明", grade: "一年级" }];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const parseDataFn = (data, list) => {
let result = [];
for (let i = 0; i < data.length; i++) {
const item = data[i];
for (const key in list[i]) {
for (let j = 0; j < item[key].length; j++) {
const nameVal = item[key][j];
result.push({ name: nameVal ,grade:list[i][key] });
}
}
}
return result;
};

let data = parseDataFn(studentsList, gradeList);

console.log(data);

条件排序

  • 有一个根据返回的时间排序的需求,比如如下数据,根据executetime正序排列
1
2
3
4
5
6
7
8
9
10
11
12
13
let jsonStr= [
{
id:'01',
executetime: 1625556657935
},{
id:'02',
executetime: 1631782536000
},
{
id:'03',
executetime: 1640854860000
}
];
  • 这里使用JavaScript sort() 方法,首先解释下这个sort的方法
1
2
// sortby: 可选,规定排序顺序。必须是函数
arrayObject.sort(sortby)
  • 如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较
  • 如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 ab,其返回值如下
    • a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值
    • a 等于 b,则返回 0
    • a 大于 b,则返回一个大于 0 的值
1
2
3
4
5
function sortByTime(a,b){
return a.executetime - b.executetime;
}
jsonStr.sort(sortByTime)
console.log(jsonStr)
  • 也可以实现倒序排列,只需要对调参数即可
1
2
3
4
5
function sortByTime(a,b){
return b.executetime - a.executetime;
}
jsonStr.sort(sortByTime)
console.log(jsonStr)

条件过滤

  • 对于后端的数据,我们需要提取有用的数据,所以需要对数据进行过滤处理
  • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
    • filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素
1
array.filter(function(currentValue,index,arr), thisValue)
1
2
3
4
5
6
// 筛选数组中的偶数
let arr = [1,2,4,5,6,7,8,9,10]
let result = arr.filter(function (num){
return num % 2 === 0;
})
console.log(result) // [2,4,6,8,10]
1
2
3
4
// 筛选获取数据中已发布的数据
let filterData = res.data.filter(function (res) {
return res.stateName == "已发布"
})
  • 利用filter,可以巧妙地去除Array的重复元素。indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了,所以重复的元素仅会保留第一个位置的元素
1
2
3
4
5
6
var result;
var arr = ["apple","strawberry","banana","pear","apple","orange","orange","strawberry"];
result = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;
});
alert(result.toString());
  • map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
1
2
3
4
5
6
let arr = [1, 4, 9, 16];
// const map = array1.map(x => x *2);
const m = arr.map(function (x) {
return x * 2;
});
console.log(m); // [2,8,18,32]
  • 实际工作中,如果遍历数据时需要唯一标识,但返回的数据没有;可以通过map对数据添加一个id属性当唯一值
1
2
3
4
5
6
let index = 0;
// 截取十条数据,并为每条数据加id
let data = res.data.splice(0,10).map(item =>{
item.id = index++;
return item
})

参数获取

  • 有时候需要原生JS获取地址栏的参数,只需要对URL字符串match一个正则,就能获取一个所有参数键值对组成的数组,并通过reduce方法把键值对按=进行分割,全都保存在一个对象身上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 获取地址栏的方法
let urlStr1 = window.location.href
let urlStr2 = document.URL
let urlStr3 = document.location.href

const url = 'http://url.com/page?username=admin&password=123';

const getURLParameters = url => (url
.match(/([^?=&]+)(=([^&]*))/g) || [] )
.reduce((a,v) => (
(a[v.slice(0,v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a)
),{});

const result = getURLParameters(url);
console.log(result);
// { username: 'admin', password: '123' }

字符串处理

字符串查找

  • 字符串方法对数据处理非常常用,关于查找有以下常用方法
方法 说明 示例
length 获取字符串长度 str.length
indexOf() 方法返回字符串中指定文本首次出现的索引(位置) str.indexOf("a")
lastIndexOf() 方法返回指定文本在字符串中最后一次出现的索引 str.lastIndexOf("a")
search() 方法搜索特定值的字符串,并返回匹配的位置 str.search('c')
charAt(index) 返回指定索引位置处的字符 str.charAt(2)
  • 如果未找到文本, indexOf()lastIndexOf() 均返回 -1;两种方法都接受作为检索起始位置的第二个参数
1
2
3
let str = "abcdefc"
// 从索引3即d开始找 第一个c的索引值为6
let pos = str.indexOf("c",3) // 6
  • lastIndexOf() 方法向后进行检索(从尾到头),这意味着:假如第二个参数是 3,则从位置 3 开始倒着检索,直到字符串的起点
1
2
3
let str = "abcdefc"
// 从倒数索引3的d开始找到起点,第一个c的索引为2
let lastPos = str.lastIndexOf("c",3) //2
  • indexOf()search()相差不大,区别在于
    • search() 方法无法设置第二个开始位置参数
    • indexOf() 方法无法设置更强大的搜索值(正则表达式)
1
2
3
// 使用正则表达式执行搜索字符串中 "w3school" 的大小写不敏感的搜索:
var str = "Visit W3School";
var n = str.search(/w3school/i); // 6

字符串截取

  • 字符串截取对数据处理非常重要,比如使用slice截取指定条数的数据等等
方法 说明 示例
slice(start, end) 截取指定起始位置的字符串,不包括end str.slice(0,1)
substring(start,end) 截取指定起始位置的字符串,不包括end str.substring(0,1)
substr(start, strLen) 截取指定长度字符串 str.substr(0,3)
split(separator,limit) 把一个字符串分割成片段创建一个字符串数组 split('',5)
  • slice方法截取不包含end,如果某个参数为负,则从字符串的结尾开始计数;如果省略第二个参数,则该方法将裁剪字符串的剩余部分
1
2
3
4
let str = "abcdefc"
// [0,3) ,不包括end
let res = str.slice(0,3) // abc
let res = str.substr(0,3) // abc
  • split方法中separator参数可以是一个字符串或一个正则表达式
1
2
3
let str = "abcd"
let res = str.split("") // ['a','b','c','d']
let res = str.split("",3) // ['a','b','c']
方法 说明 示例
replace() 用另一个值替换在字符串中指定的值 str.replace('c','d')
concat() 连接两个或多个字符串 str.concat(str1,str2)
toUpperCase() 字符串转换为大写 str.toUpperCase()
toLowerCase() 字符串转换为小写 str.toLowerCase()
trim() 方法删除字符串两端的空白符 str.trim()
  • replace方法不会改变调用它的字符串。它返回的是新字符串。并且只替换首个匹配;对大小写敏感,如需执行大小写不敏感的替换,请使用正则表达式 /i
1
2
3
let str = "abcdefc"
// 默认大小写敏感是匹配不到cd的
let res = str.replace(/CD/i,'g') // "abgefc"
  • 请注意正则表达式不带引号。如需替换所有匹配,请使用正则表达式的 g 标志(用于全局搜索):
1
2
3
let str = "abcdefc"
// 默认只替换首个匹配的
let res = str.replace(/c/g,'g') // bcgdefg

数组处理

数组遍历

  • 数组遍历有以下几种遍历方法,各有优缺点
1
2
3
4
5
6
7
8
9
// 普通for循环
for(let i = 0; i < arr.length; i++){
console.log(arr[i])
}

// 优化版 使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显
for (let i = 0, length = arr.length; i < length; i++) {
console.log(arr[i]);
}
  • 使用forEach()循环遍历数组,接收一个回调函数
1
2
3
4
5
6
7
8
// 遍历数组 item参数是数组中的内容,index为其索引,array表示数组本身
arr.forEach((item,index,array)=>{
console.log(item);
})
// 遍历对象
obj.forEach((item)=>{
console.log(item.id);
})
  • 使用map()方法,原数组被映射成对应新数组
1
2
3
4
let newArr = arr.map((value, index)=>{
console.log(value+'--'+index);
return value + 10;
})
  • for....in 方法是es5标准, 此方法遍历数组效率低,主要是用来循环遍历对象的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(let item in arr){
// item为索引
console.log(item+"--"+arr[item]);
}

// 循环遍历对象的属性,js中动态获取key,得到某对象中相对应的value = obj[key]
let obj = [{ a: 1 }, { b: 2 }, { c: 3 }];

for (let key in obj) {
console.log(key, obj[key]);
}
// 0 { a: 1 }
// 1 { b: 2 }
// 2 { c: 3 }
  • for...of方法ES6支持
1
2
3
4
for(let item of arr) {
// item不再是索引,而是每一项的结果
console.log(item);
}

数组处理

  • 数组有以下常用的方法
方法 说明 示例
toString() 数组转换为数组值(逗号分隔)的字符串 arr.toString()
join() 数组转为一某字符分隔的字符串 arr.join("*")
1
2
3
let arr = ["Banana", "Orange", "Apple", "Mango"];
let str = arr.toString(); // Banana,Orange,Apple,Mango
let res = arr.join("-"); // Banana-Orange-Apple-Mango
方法 说明 示例
pop() 删除数组最后一个元素,返回删除的值 arr.pop(value)
push() 在数组结尾处添加一个新的元素,返回新数组的长度 arr.push(value)
shift() 删除首个数组元素 arr.shift()
unshift() 在数组开头添加新元素 arr.unshift(value)
slice(start,end) 截取指定索引的数组 arr.slice(0,2)
sort() 数组排序 arr.sort()
  • 默认情况下sort方法是按ASCII字母顺序排序的,而非我们认为是按数字大小排序
1
2
3
4
arr.sort(function (a, b) {
return a - b; // 从小到大
// b - a; 从大到小
});