本文已参与「新人创作礼」活动,一起开启掘金创作之路。
二、数据处理
1. 实现数组的乱序排序
- 使用sort,对数组进行排序
- 每次从数组中挑选两个值进行
- 大于等于0不交换位置
- 小于0进行交换
- Math.random() - 0.5大于小于0是随机出现的,所以数组随机排序
function shuffle(arr) {
arr.sort(() => {
Math.random() - 0.5
})
return arr;
}
const arr = [1,2,3,4,5,6,7,8,9,10];
console.log(shuffle(arr))
- 使用Math.random()随机生成索引值
- 将第一个元素与随机生成的索引值的元素进行交换
- 将第二个元素与随机生成的索引值的元素进行交换
- 按照上边规律直到遍历完
function shuffle(arr) {
for (let i = 0; i < arr.length; i++) {
var randomIndex = parseInt(arr.length * Math.random());
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
console.log(arr)
}
return arr;
}
const arr = [1,2,3,4,5,6,7,8,9,10];
console.log(shuffle(arr)) // [2, 3, 6, 5, 8, 9, 7, 1, 4, 10]
2. 实现数组元素求和
- 一维数组
- 循环求和
function arrSum(arr) {
let sum = 0
arr.forEach(item => {
sum += item
})
return sum;
}
const arr = [1,3,6,9]
console.log(arrSum(arr)) // 19
- 使用reduce求和,reduce是将上次得到的结果作为这次的参数
let sum = arr.reduce((total,i) => {
return total + i
})
const arr = [1,3,6,9]
console.log(sum); // 19
- 多维数组
- 先拍平载再求和
const arr1 = [1,[3,[6]],9]
let sum1 = arr1.flat(Infinity).reduce((prev,next) => {
return prev + next
},0)
console.log(sum1);
- 对象数组
let arr2 = [{a:1, b:3, c:6}, {a:3, b:3}, {a:3}]
let sum2 = arr2.reduce((prev,next) => {
return prev + next['a']
},0)
console.log(sum2) // 7
3. 实现数组扁平化
- es6中的flat
function flatten(arr) {
// arr.flat(Infinity) 不论多少层都拍平
return arr.flat(2)
}
const arr = [1,[2,[3,4]],5]
console.log(flatten(arr)) // [1, 2, 3, 4,5]
- 扩展运算符:只能展开一层数组
- 若arr中含有数组则使用一次扩展运算符,直至没有为止
function flatten(arr) {
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
}
return arr;
}
const arr = [1,[2,[3,4]],5]
console.log(flatten(arr)) // [1, 2, 3, 4,5]
- 普通递归
- 通过遍历查找是否还有数组,有数组进行递归,不是数组的话,push到结果中
function flatten(arr) {
let res = [];
arr.forEach(item => {
if (Array.isArray(item)) {
res = res.concat(flatten(item))
}
else {
res.push(item)
}
});
return res;
}
const arr = [1,[2,[3,4]],5]
console.log(flatten(arr)) // [1, 2, 3, 4,5]
实现数组的flat方法
- reduce + 递归
- 上次的结果作为参数传递,最后返回prev
function flatten(arr) {
return arr.reduce((prev,next) => {
return prev.concat(Array.isArray(next) ? flatten(next) : next)
},[])
}
const arr = [1,[2,[3,4]],5]
console.log(flatten(arr)) // [1, 2, 3, 4,5]
- toString转为字符串,再split转为数组
function flatten(arr) {
let res = arr.join(',').split(',');
return res
}
const arr = [1,[2,[3,4]],5]
console.log(flatten(arr)) // [1, 2, 3, 4,5]
4. 实现数组去重
- new Set,可对NaN去重
function unique(arr) {
return [...new Set(arr)]
}
const arr = [1,2,1,NaN,undefined,NaN,undefined,true,'abc',true,'abc']
console.log(unique(arr))
// [1, 2, NaN, undefined, true, 'abc']
- 使用indexOf判断res中是否存在,无法对NaN去重
function unique(arr) {
let res = [];
arr.forEach(item => {
if (res.indexOf(item) === -1) {
res.push(item)
}
})
return res;
}
const arr = [1,2,1,NaN,undefined,NaN,undefined,true,'abc',true,'abc']
console.log(unique(arr))
// [1, 2, NaN, undefined, NaN, true, 'abc']
- 使用includes判断res中是否存在,可对NaN去重
function unique(arr) {
let res = [];
arr.forEach(item => {
if (!res.includes(item)) {
res.push(item)
}
})
return res;
}
const arr = [1,2,1,NaN,undefined,NaN,undefined,true,'abc',true,'abc']
console.log(unique(arr))
// [1, 2, NaN, undefined, true, 'abc']
- 使用map,has()判断是否存在,不存在set()添加,可对NaN去重
function unique(arr) {
const map = new Map();
const res = [];
arr.forEach(item => {
// 判断是否包含item的属性值
if (!map.has(item)) {
// 将item设置到map中,并设置属性值为true
map.set(item, true)
res.push(item)
}
})
return res;
}
const arr = [1,2,1,NaN,undefined,NaN,undefined,true,'abc',true,'abc']
console.log(unique(arr))
// [1, 2, NaN, undefined, true, 'abc']
5. 实现数组push方法
- push方法传参参数不确定,使用arguments
- push返回数组长度,并将值添加到数组中
Array.prototype.myPush = function() {
// 判断是否有this.length
if (!this.length) {
this.length = 0;
}
if (isNaN(Number(this.length))) {
this.length = 0;
}
for (let i = 0;i < arguments.length; i++) {
this[this.length] = arguments[i]
}
return this.length;
}
const arr = [1,'abc']
console.log(arr.myPush(3,'aa'),arr) // [1, 'abc', 3, 'aa']
console.log(arr.push(3,'aa'),arr)
6. 实现数组的filter方法
- filter传入一个带有条件的函数
- 返回能满足此条件的值数组
- filter不会改变原数组
Array.prototype.myFilter = function(fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数')
}
const res = [];
for(let i = 0;i < this.length;i++) {
// 满足条件将this[i]添加到结果中
fn(this[i]) && res.push(this[i]);
}
return res;
}
const arr = [1,3,6,9]
const res = arr.myFilter(item => item > 3 && item < 9) // [6]
// const res = arr.filter(item => item > 3 && item < 9)
console.log(res)
7. 实现数组的map方法
- map传入函数
- 返回一个数组
- map不会改变原数组
Array.prototype.myMap = function(fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数')
}
const res = [];
for(let i = 0;i < this.length;i++) {
res.push(fn(this[i]))
}
return res;
}
const arr = [1,2,3,5]
const res = arr.myMap(item => {
return item * 2
})
// const res = arr.map(item => {
// return item * 2
// })
console.log(res) // [2, 4, 6, 10]
8. 实现类数组转化为数组
类数组具备两个特性:
- 数字作为属性名称
- 具有length属性
类数组设计的目的更多的是遍历和访问下标,而不是添加和删除元素
- 调用数组的slice
const arrayLike = {
0: '类数组',
1: '数字作为属性名称',
2: '具备length属性',
length: 3
}
const res = Array.prototype.slice.call(arrayLike);
console.log(res) // ['类数组', '数字作为属性名称', '具备length属性']
- 调用数组的splice
const arrayLike = {
0: '类数组',
1: '数字作为属性名称',
2: '具备length属性',
length: 3
}
const res = Array.prototype.splice.call(arrayLike, 0);
console.log(res) // ['类数组', '数字作为属性名称', '具备length属性']
- Array.from
const arrayLike = {
0: '类数组',
1: '数字作为属性名称',
2: '具备length属性',
length: 3
}
const res = const res = Array.from(arrayLike);
console.log(res) // ['类数组', '数字作为属性名称', '具备length属性']
- 调用数组的concat,使用apply
const arrayLike = {
0: '类数组',
1: '数字作为属性名称',
2: '具备length属性',
length: 3
}
const res = Array.prototype.concat.apply([], arrayLike);
console.log(res) // ['类数组', '数字作为属性名称', '具备length属性']
9. 实现字符串翻转
function myReverse(str) {
if (typeof str !== 'string') {
throw Error('参数必须是一个字符串')
}
return str.split('').reverse().join('')
}
const str = 'abc';
console.log(myReverse(str)) // 'cba'
10. 实现字符串的repeat方法
repeat(count)方法是重复填写count个str
- new Array(count + 1)表示数组长度为count + 1的空数组
- 通过str分隔转为字符串
function myRepeat(str, count) {
return new Array(count + 1).join(str);
}
const str = 'abc';
console.log(myRepeat(str,2)) // 'abcabc'
// console.log(str.repeat(2))
11. 解析url变为对象形式
- 手写函数
function query(url) {
let args = url.split('?');
if (args[0] === url) {
return ''
}
let params = args[1].split('&')
let res = {}
params.forEach(item => {
let arr = item.split('=');
const key = arr[0];
const val = arr[1];
res[key] = val;
})
return res;
}
console.log(query("http://127.0.0.1/learnjs/index.html?a=1&b=&=3"));
// {a: '1', b: '', "": '3'}
12. 交换a,b的值
- es6解构赋值
let a = 1;
let b = 2;
[a,b] = [b,a]
console.log(a,b) // 2 1
- 临时变量
let a = 1;
let b = 2;
let tmp = a;
a = b;
b = tmp;
console.log(a,b) // 2 1
- 使用加法
let a = 1;
let b = 2;
a = a + b;
b = a - b;
a = a - b;
console.log(a,b) // 2 1
13. 将数字每千分位用逗号隔开
- 使用toLocaleString()方法
function handleNum(num) {
return num.toLocaleString();
}
const num = 1010101.0246;
// const num = 100 // 100
console.log(handleNum(num)) // 1,010,101.025
- 手写函数
function handleNum(n) {
let num = n.toString();
let res;
// 判断有无小数点,有的话加上.
let decimals = '';
if (num.indexOf('.') > -1) {
decimals = `.${num.split('.')[1]}`;
res = num.split('.')[0];
}
else {
decimals = '';
res = num;
}
// 根据长度对3取余,余数+中间+小数
let length = res.length;
if (length <= 3) {
return num;
}
else {
let remainder = res % 3;
const start = res.slice(0, remainder);
// \d{n}几位数字,/g返回所有的匹配
const middle = res.slice(remainder, length).match(/\d{3}/g).join(',');
return `${start},${middle}${decimals}`;
}
}
const num = 1010101.0246;
// const num = 100 // 100
console.log(handleNum(num)) // 1,010,101.0246
14. 实现非负大整数相加
大整数,超过Number.MAX_SAFE_INTEGER的整数进行相加,使用+不行,超过Number.MAX_SAFE_INTEGER数字会被立即转换为科学计数法,并且数字精度相比以前将会有误差
- 取两个值的最大长度,另一个用0补齐
- 从个位数开始相加singularSum
- 计算进位数,Math.floor(singularSum/10) carry
- 将此次结果保存到字符串中
- 循环到相加完成
function sumBigNumber(a,b) {
let maxLength = Math.max(a.length,b.length);
a = a.padStart(maxLength, 0);
b = b.padStart(maxLength, 0);
let sum = '';
let singularSum = 0; // 单数相加结果
let carry = 0; // 进位
for (let i = maxLength -1;i>= 0; i--) {
singularSum = parseInt(a[i]) + parseInt(b[i]) + carry;
carry = Math.floor(singularSum/10);
sum = singularSum % 10 + sum;
}
if (carry == 1) {
sum = "1" + sum;
}
return sum;
}
const num1 = '9007199254740991';
const num2 = '1234567899999999999';
console.log(sumBigNumber(num1,num2)); // 1243575099254740990
15. add(1)(2)(3)柯里化
柯里化是函数编程中的概念,主要是传递给参数一部分参数,返回一个函数接着来处理剩下的参数,直到处理完所有的参数
function add (...args) {
return args.reduce((prev,next) => {
return prev + next;
}, 0)
}
function currying(fn) {
let args = [];
return function tmp(...newArgs) {
if (newArgs.length) {
args = [...args,...newArgs];
return tmp;
}
else {
let val = fn.apply(this, args);
args = [];
return val;
}
}
}
let addCurry = currying(add)
console.log(addCurry(1)(2)(3)(4)()); // 10
console.log(addCurry(1)(2)(3,4)()); // 10
console.log(addCurry(1)(2,3,4)()); // 10
16. 使用es5,es6求函数参数的和
- es5
function sum() {
let sum = 0;
Array.prototype.forEach.call(arguments, item => {
sum += item;
})
return sum;
}
console.log(sum(1,2,3)) // 6
- es6
function sum(...args) {
let sum = 0;
args.forEach(item => {
sum += item;
})
return sum;
}
console.log(sum(1,2,3)) // 6
17. 实现日期格式化函数
function dateFormat(dateInput,format) {
let day = dateInput.getDate();
let month = dateInput.getMonth() + 1;
let year = dateInput.getFullYear();
format = format.replace(/yyyy/, year);
format = format.replace(/mm/, month);
format = format.replace(/dd/, day);
return format;
}
console.log(dateFormat(new Date('2022-1-1'), 'yyyy/mm/dd')) // 2022/1/1
console.log(dateFormat(new Date('2022-1-1'), 'yyyy年mm月dd日')) // 2022年1月1日
18. 将js对象转为树形结构
在项目中,有时需要将后端返回的列表集合按照父子转为树形结构
转换前后:
const jsonLike = [
{
id: 1,
pid: 0,
name: 'body'
},
{
id: 2,
pid: 1,
name: 'div'
},
{
id: 3,
pid: 2,
name: 'title'
}
]
console.log(jsonToTree(jsonLike))
// 转换后结果:
tree = [{
id: 1,
pid: 0,
name: 'body',
children: [{
id: 2,
pid: 1,
name: 'div',
children: [{
id: 3,
pid: 2,
name: 'title'
}]
}
}]
代码实现:
function jsonToTree(data) {
let res = [];
if (!Array.isArray(data)) {
return res;
}
let map = {};
data.forEach(item => {
map[item.id] = item;
})
data.forEach(item => {
let parent = map[item.pid];
if (parent) {
if (!parent['children']) {
parent['children'] = []
}
parent['children'].push(item);
}
else {
res.push(item);
}
})
return res;
}