常规数据扁平化
方案一:基于ES10中新增的flat实现扁平化处理
let arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, 14]]], 10];
let flatArr = arr.flat(Infinity);
console.log(flatArr); //[1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
方案二:基于concat方式进行逐级降维
while (arr.some((item) => Array.isArray(item))) {
// arr = [].concat(...arr);
arr = [].concat.apply([], arr); // 兼容IE
}
console.log(arr);
方案三:执行toString变为字符串的时候就已经扁平化了「不适合某一项是对象,因为对象会变为“[object Object]”」
arr = arr.toString().split(',').map(Number);
console.log(arr);
方案四:基于递归实现
Object.defineProperty(Array.prototype, 'myFlat', {
writable: true,
enumerable: false,
configurable: true,
value: function myFlat() {
let result = [];
let next = function next(arr) {
arr.forEach((item) => {
if (Array.isArray(item)) {
next(item);
} else {
result.push(item);
}
});
};
next(this);
return result;
},
});
let flatArr = arr.myFlat();
console.log(flatArr);
特殊数组扁平化
方案一:递归查找「深度优先原则」
let address = [
{
code: '110000',
name: '北京市',
children: [
{
code: '110100',
name: '北京市',
children: [
{
code: '110101',
name: '东城区',
},
{
code: '110102',
name: '西城区',
},
{
code: '110105',
name: '朝阳区',
},
],
},
],
},
{
code: '310000',
name: '上海市',
children: [
{
code: '310100',
name: '上海市',
children: [
{
code: '310101',
name: '黄浦区',
},
{
code: '310104',
name: '徐汇区',
},
],
},
],
},
{
code: '440000',
name: '广东省',
children: [
{
code: '440100',
name: '广州市',
children: [
{
code: '440103',
name: '荔湾区',
},
{
code: '440104',
name: '越秀区',
},
],
},
],
},
{
code: '330000',
name: '浙江省',
children: [
{
code: '330100',
name: '杭州市',
children: [
{
code: '330102',
name: '上城区',
},
{
code: '330103',
name: '下城区',
},
],
},
],
},
];
const getNameByCode = (address, code) => {
let result = '';
const next = (arr) => {
for (let i = 0; i < arr.length; i++) {
let { code: codeItor, name, children } = arr[i];
if (codeItor === code) {
result = name;
break;
}
if (children && children.length > 0) {
next(children);
}
}
};
next(address);
return result;
};
console.log(getNameByCode(address, '440103')); // 荔湾区
方案二:先把数组扁平化,后期查找的时候按照扁平化的数组查找即可
const getNameByCode = (function (address) {
// 先扁平化处理「广度优先原则」
let result = [];
const next = (arr) => {
// 先把本级处理好
let temp = arr.map((item) => {
return {
code: item.code,
name: item.name,
};
});
result = result.concat(temp);
// 再看是否有children,如果存在,再处理下一级
arr.forEach((item) => {
if (item.children && item.children.length > 0) next(item.children);
});
};
next(address);
// 返回按照code查找的方法 「去扁平话后的数据中查找」
return function getNameByCode(code) {
let item = result.find((item) => item.code === code);
return item ? item.name : '';
};
})(address);
console.log(getNameByCode('440103')); // 荔湾区
对象扁平化处理
const obj = {
a: {
b: 1,
c: 2,
d: {
e: 3,
2: 200,
},
},
b: [1, 2, { a: 3, b: 4 }],
c: 1,
1: 100,
x: {},
};
Object.defineProperty(Object.prototype, 'flatten', {
enumerable: false,
writable: true,
configurable: true,
value: function flatten(obj) {
let result = {};
const next = (obj, attr) => {
// isPlainObject判断是否是纯粹对象
let isObject = _.isPlainObject(obj),
isArray = Array.isArray(obj);
if (isObject || isArray) {
// isEmptyObject判断是否为空对象
if (isObject && _.isEmptyObject(obj)) {
result[attr] = obj;
return;
}
if (isArray && obj.length === 0) {
result[attr] = obj;
return;
}
// 不为空的情况则迭代处理 each迭代方法的封装
_.each(obj, (value, key) => {
let temp = isNaN(key) ? `.${key}` : `[${key}]`;
next(value, attr ? attr + temp : key);
});
return;
}
result[attr] = obj;
};
next(obj, '');
return result;
},
});
console.log(Object.flatten(obj));
/* // 输出结果:
let objFlatten = {
1: 100,
'a.b': 1,
'a.c': 2,
'a.d.e': 3,
'a.d[2]': 200,
'b[0]': 1,
'b[1]': 2,
'b[2].a': 3,
'b[2].b': 4,
x: {},
}; */
常规方法
/* 函数数据类型检测 */
var getProto = Object.getPrototypeOf;
var class2type = {};
var toString = class2type.toString; // Object.prototype.toString 检测数据类型的
var hasOwn = class2type.hasOwnProperty; // Object.prototype.hasOwnProperty 检测是否是对象自身属性
var fnToString = hasOwn.toString; // Function.prototype.toString 把函数转换为字符串
var ObjectFunctionString = fnToString.call(Object); // 把Object函数转换为字符串 Object.toString() => "function Object() { [native code] }"
// 检测是否为空对象
var isEmptyObject = function isEmptyObject(obj) {
let keys = Object.getOwnPropertyNames(obj);
if (typeof Symbol !== 'undefined')
keys = keys.concat(Object.getOwnPropertySymbols(obj));
return keys.length === 0;
};
// 检测是否为纯对象/标准普通对象 : obj.__proto__ === Object.prototype
var isPlainObject = function isPlainObject(obj) {
let proto, Ctor;
if (!obj || toString.call(obj) !== '[object Object]') {
return false;
}
proto = getProto(obj);
// 匹配 Object.create(null) 创建的对象
if (!proto) {
return true;
}
Ctor = hasOwn.call(proto, 'constructor') && proto.constructor; // 获取对象原型上的constructor属性
return (
typeof Ctor === 'function' &&
fnToString.call(Ctor) === ObjectFunctionString
);
};
// 迭代数组、类数组、纯粹对象,支持迭代的结束
const each = function each(obj, callback) {
let isArray = isArrayLike(obj),
isObject = isPlainObject(obj);
if (!isArray && !isObject)
throw new TypeError('obj must be an array or object');
if (!isFunction(callback))
throw new TypeError('callback must be a function');
if (isArray) {
// 数组和类数组的迭代
for (let i = 0, len = obj.length; i < len; i++) {
let item = obj[i],
index = i;
if (callback.call(item, item, index) === false) break;
}
return obj;
}
// 对象的迭代
let keys = Object.getOwnPropertyNames(obj);
if (typeof Symbol !== 'undefined')
keys = keys.concat(Object.getOwnPropertySymbols(obj));
for (let i = 0, len = keys.length; i < len; i++) {
let key = keys[i],
value = obj[key];
if (callback.call(value, value, key) === false) break;
}
return obj;
};
let obj = {
a: {
b: {
c: {
d: 1,
},
},
},
};
// 可选链处理
const getValue = (objName, keyPath) => {
keyPath = keyPath.replace(/\./g, '?.');
return eval(`${objName}?.${keyPath}`);
};
console.log(getValue('obj', 'a.b.c.d')); // 1
console.log(getValue('obj', 'a.b')); // { c: { d: 1 } }
// reduce
const getValue = (obj, keyPath) => {
let keys = keyPath.split('.');
return keys.reduce((pre, cur) => {
return pre ? pre[cur] : undefined;
}, obj);
};
console.log(getValue(obj, 'a.b.c.d')); // 1
console.log(getValue(obj, 'a.b')); // { c: { d: 1 } }
// 递归
const getValue = (obj, keyPath) => {
let keys = keyPath.split('.'),
index = 0,
result;
const next = (x) => {
if (index >= keys.length) return x;
let key = keys[index++];
result = x[key];
if (result == null || typeof result !== 'object') return;
next(result);
};
next(obj);
return result;
};
console.log(getValue(obj, 'a.b.c.d')); // 1
console.log(getValue(obj, 'a.b')); // { c: { d: 1 } }
一维数据转数结构
方案一只能处理一级结构
let data = [
{ id: 0, parentId: null, text: '北京市' },
{ id: 1, parentId: 0, text: '海淀区' },
{ id: 4, parentId: 0, text: '朝阳区' },
{ id: 7, parentId: null, text: '上海市' },
{ id: 8, parentId: 7, text: '浦东新区' },
{ id: 11, parentId: null, text: '广州市' },
{ id: 12, parentId: 11, text: '天河区' },
{ id: 15, parentId: null, text: '深圳市' },
];
/* 方案一 (O(N^2)) */
const list2Tree = (data) => {
// 先找出第一级列表
let arr = data.filter((item) => item.parentId === null);
// 递归第一级的数据,筛选出其子集数据
arr.forEach((item) => {
let children = data.filter((cur) => cur.parentId === item.id);
if (children.length > 0) {
item.children = children;
}
});
return arr;
};
console.log(list2Tree(data));
方案二:利用map数据结构数据 0(N) 可以处理多级结构
/* 方案二:利用map数据结构数据 */
const list2Tree = (data) => {
// 把data变为Map数据结构
let map = new Map();
data.forEach((item) => {
map.set(item.id, item);
});
let arr = [];
// 迭代数组中的每一项,根据parentId做不同的处理
data.forEach((item) => {
// parentId为null的,就是第一级,直接加入到arr中即可
if (item.parentId === null) {
arr.push(item);
} else {
// parentId不为null的,就是子集,需要找到其父级,把当前项加入到父级的children中
let parent = map.get(item.parentId);
parent.children ? parent.children.push(item) : (parent.children = [item]);
}
});
return arr;
};
console.log(list2Tree(data));
数据组合问题
const obj = {
data: [
['xiaoming', 'male', '18', 'beijing', '2020-01-02'],
['xiaohong', 'female', '20', 'shanghai', '2020-01-03'],
],
columns: [
{ name: 'name', note: '' },
{ name: 'gender', note: '' },
{ name: 'age', note: '' },
{ name: 'address', note: '' },
{ name: 'date', note: '' },
],
};
const combine = (obj) => {
const { data, columns } = obj,
columnKeys = {};
// 先把columns变为{name:0,gender:1...} 这种格式
columns.forEach((item, index) => {
columnKeys[item.name] = index;
});
// 外层迭代数据data
return data.map((item) => {
// item:['xiaoming', 'male', '18', 'beijing', '2020-01-02']
// columnKeys:{name:0,gender:1, age:2,address:3,date:4}
let obj = {};
_.each(columnKeys, (index, key) => {
obj[key] = item[index];
});
return obj;
});
};
console.log(combine(obj));
/* // 输出下面结果:
[
{
name: 'xiaoming',
gender: 'male',
age: '18',
address: 'beijing',
date: '2020-01-02',
},
{
name: 'xiaohong',
gender: 'female',
age: '20',
address: 'shanghai',
date: '2020-01-03',
},
]; */
const combine = (obj) => {
let { data, columns } = obj;
// 把column按照列的字段名扁平化
columns = columns.map((item) => item.name); // ['name', 'gender', 'age', 'address', 'date']
return data.map((item) => {
let obj = {};
columns.forEach((key, index) => {
obj[key] = item[index];
});
return obj;
});
};
console.log(combine(obj));
const obj = {
a: {
b: 1,
c: 2,
d: {
e: 3,
2: 200,
},
},
b: [1, 2, { a: 3, b: 4 }],
c: 1,
1: 100,
x: {},
};
// 转换为JSON字符串
// console.log(JSON.stringify(obj));
// 转换为URLENCODED格式字符串「有的后台要求AJAX请求主体传递这样的格式」
// 利用qs库
// 'a[b]=1&a[c]=2&a[d][e]=3&a[d][2]=200&b[0]=1&b[1]=2&b[2][a]=3&b[2][b]=4&c=1&1=100&x='
console.log(qs.stringify(obj)); //1=100&a[b]=1&a[c]=2&a[d][2]=200&a[d][e]=3&b[0]=1&b[1]=2&b[2][a]=3&b[2][b]=4&c=1