🌈个人主页:前端青山
🔥系列专栏:JavaScript篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript- 数据处理
目录
1. 实现日期格式化函数
2. 交换a,b的值,不能用临时变量
3. 实现数组的乱序输出
4. 实现数组元素求和
5. 实现数组的扁平化
6. 实现数组去重
7. 实现数组的flat方法
8. 实现数组的push方法
9. 实现数组的filter方法
10. 实现数组的map方法
11. 实现字符串的repeat方法
12. 实现字符串翻转
13. 将数字每千分位用逗号隔开
14. 实现非负大整数相加
13. 实现 add(1)(2)(3)
14. 实现类数组转化为数组
15. 使用 reduce 求和
16. 将js对象转化为树形结构
17. 使用ES5和ES6求函数参数的和
18. 解析 URL Params 为对象
1. 实现日期格式化函数
输入:
dateFormat(new Date('2020-12-01'), 'yyyy/MM/dd') // 2020/12/01
dateFormat(new Date('2020-04-01'), 'yyyy/MM/dd') // 2020/04/01
dateFormat(new Date('2020-04-01'), 'yyyy年MM月dd日') // 2020年04月01日
复制代码
const dateFormat = (dateInput, format)=>{var day = dateInput.getDate() var month = dateInput.getMonth() + 1 var year = dateInput.getFullYear() format = format.replace(/yyyy/, year)format = format.replace(/MM/,month)format = format.replace(/dd/,day)return format
}
复制代码
2. 交换a,b的值,不能用临时变量
巧妙的利用两个数的和、差:
a = a + b
b = a - b
a = a - b
复制代码
3. 实现数组的乱序输出
主要的实现思路就是:
-
取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行交换。
-
第二次取出数据数组第二个元素,随机产生一个除了索引为1的之外的索引值,并将第二个元素与该索引值对应的元素进行交换
-
按照上面的规律执行,直到遍历完成
a = a + b
b = a - b
a = a - b
复制代码
还有一方法就是倒序遍历:
var arr = [1,2,3,4,5,6,7,8,9,10];
let length = arr.length,randomIndex,temp;while (length) {randomIndex = Math.floor(Math.random() * length--);temp = arr[length];arr[length] = arr[randomIndex];arr[randomIndex] = temp;}
console.log(arr)
复制代码
4. 实现数组元素求和
-
arr=[1,2,3,4,5,6,7,8,9,10],求和
let arr=[1,2,3,4,5,6,7,8,9,10]
let sum = arr.reduce( (total,i) => total += i,0);
console.log(sum);
复制代码
-
arr=[1,2,3,[[4,5],6],7,8,9],求和
var = arr=[1,2,3,[[4,5],6],7,8,9]
let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);
console.log(arr);
复制代码
递归实现:
let arr = [1, 2, 3, 4, 5, 6]
function add(arr) {if (arr.length == 1) return arr[0] return arr[0] + add(arr.slice(1))
}
console.log(add(arr)) // 21
复制代码
5. 实现数组的扁平化
(1)递归实现
普通的递归思路很容易理解,就是通过循环递归的方式,一项一项地去遍历,如果每一项还是一个数组,那么就继续往下遍历,利用递归程序的方法,来实现数组的每一项的连接:
let arr = [1, [2, [3, 4, 5]]];
function flatten(arr) {let result = [];
for(let i = 0; i < arr.length; i++) {if(Array.isArray(arr[i])) {result = result.concat(flatten(arr[i]));} else {result.push(arr[i]);}}return result;
}
flatten(arr); // [1, 2, 3, 4,5]
复制代码
(2)reduce 函数迭代
从上面普通的递归函数中可以看出,其实就是对数组的每一项进行处理,那么其实也可以用reduce 来实现数组的拼接,从而简化第一种方法的代码,改造后的代码如下所示:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {return arr.reduce(function(prev, next){return prev.concat(Array.isArray(next) ? flatten(next) : next)}, [])
}
console.log(flatten(arr));// [1, 2, 3, 4,5]
复制代码
(3)扩展运算符实现
这个方法的实现,采用了扩展运算符和 some 的方法,两者共同使用,达到数组扁平化的目的:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {while (arr.some(item => Array.isArray(item))) {arr = [].concat(...arr);}return arr;
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
复制代码
(4)split 和 toString
可以通过 split 和 toString 两个方法来共同实现数组扁平化,由于数组会默认带一个 toString 的方法,所以可以把数组直接转换成逗号分隔的字符串,然后再用 split 方法把字符串重新转换为数组,如下面的代码所示:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {return arr.toString().split(',');
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
复制代码
通过这两个方法可以将多维数组直接转换成逗号连接的字符串,然后再重新分隔成数组。
(5)ES6 中的 flat
我们还可以直接调用 ES6 中的 flat 方法来实现数组扁平化。flat 方法的语法:arr.flat([depth])
其中 depth 是 flat 的参数,depth 是可以传递数组的展开深度(默认不填、数值是 1),即展开一层数组。如果层数不确定,参数可以传进 Infinity,代表不论多少层都要展开:
let arr = [1, [2, [3, 4]]];
function flatten(arr) {return arr.flat(Infinity);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
复制代码
可以看出,一个嵌套了两层的数组,通过将 flat 方法的参数设置为 Infinity,达到了我们预期的效果。其实同样也可以设置成 2,也能实现这样的效果。在编程过程中,如果数组的嵌套层数不确定,最好直接使用 Infinity,可以达到扁平化。 (6)正则和 JSON 方法 在第4种方法中已经使用 toString 方法,其中仍然采用了将 JSON.stringify 的方法先转换为字符串,然后通过正则表达式过滤掉字符串中的数组的方括号,最后再利用 JSON.parse 把它转换成数组:
let arr = [1, [2, [3, [4, 5]]], 6];
function flatten(arr) {let str = JSON.stringify(arr);str = str.replace(/(\[|\])/g, '');str = '[' + str + ']';return JSON.parse(str);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
复制代码
6. 实现数组去重
给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。
ES6方法(使用数据结构集合):
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
复制代码
ES5方法:使用map存储不重复的数字
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
uniqueArray(array); // [1, 2, 3, 5, 9, 8]
function uniqueArray(array) {let map = {};let res = [];for(var i = 0; i < array.length; i++) {if(!map.hasOwnProperty([array[i]])) {map[array[i]] = 1;res.push(array[i]);}}return res;
}
复制代码
7. 实现数组的flat方法
function _flat(arr, depth) {if(!Array.isArray(arr) || depth <= 0) {return arr;}return arr.reduce((prev, cur) => {if (Array.isArray(cur)) {return prev.concat(_flat(cur, depth - 1))} else {return prev.concat(cur);}}, []);
}
复制代码
8. 实现数组的push方法
let arr = [];
Array.prototype.push = function() {for( let i = 0 ; i < arguments.length ; i++){this[this.length] = arguments[i] ;}return this.length;
}
复制代码
9. 实现数组的filter方法
Array.prototype._filter = function(fn) {if (typeof fn !== "function") {throw Error('参数必须是一个函数');}const res = [];for (let i = 0, len = this.length; i < len; i++) {fn(this[i]) && res.push(this[i]);}return res;
}
复制代码
10. 实现数组的map方法
Array.prototype._map = function(fn) {if (typeof fn !== "function") {throw Error('参数必须是一个函数');}const res = [];for (let i = 0, len = this.length; i < len; i++) {res.push(fn(this[i]));}return res;
}
复制代码
11. 实现字符串的repeat方法
输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。
function repeat(s, n) {return (new Array(n + 1)).join(s);
}
复制代码
递归:
function repeat(s, n) {return (n > 0) ? s.concat(repeat(s, --n)) : "";
}
复制代码
12. 实现字符串翻转
在字符串的原型链上添加一个方法,实现字符串翻转:
String.prototype._reverse = function(a){return a.split("").reverse().join("");
}
var obj = new String();
var res = obj._reverse ('hello');
console.log(res); // olleh
复制代码
需要注意的是,必须通过实例化对象之后再去调用定义的方法,不然找不到该方法。
13. 将数字每千分位用逗号隔开
数字有小数版本:
let format = n => {let num = n.toString() // 转成字符串let decimals = ''// 判断是否有小数num.indexOf('.') > -1 ? decimals = num.split('.')[1] : decimalslet len = num.lengthif (len <= 3) {return num} else {let temp = ''let remainder = len % 3decimals ? temp = '.' + decimals : tempif (remainder > 0) { // 不是3的整数倍return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp} else { // 是3的整数倍return num.slice(0, len).match(/\d{3}/g).join(',') + temp }}
}
format(12323.33) // '12,323.33'
复制代码
数字无小数版本:
let format = n => {let num = n.toString() let len = num.lengthif (len <= 3) {return num} else {let remainder = len % 3if (remainder > 0) { // 不是3的整数倍return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') } else { // 是3的整数倍return num.slice(0, len).match(/\d{3}/g).join(',') }}
}
format(1232323) // '1,232,323'
复制代码
14. 实现非负大整数相加
JavaScript对数值有范围的限制,限制如下:
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MIN_VALUE // 5e-324
Number.MIN_SAFE_INTEGER // -9007199254740991
复制代码
如果想要对一个超大的整数(> Number.MAX_SAFE_INTEGER
)进行加法运算,但是又想输出一般形式,那么使用 + 是无法达到的,一旦数字超过 Number.MAX_SAFE_INTEGER
数字会被立即转换为科学计数法,并且数字精度相比以前将会有误差。
实现一个算法进行大数的相加:
function sumBigNumber(a, b) {let res = '';let temp = 0;a = a.split('');b = b.split('');while (a.length || b.length || temp) {temp += ~~a.pop() + ~~b.pop();res = (temp % 10) + res;temp = temp > 9}return res.replace(/^0+/, '');
}
复制代码
其主要的思路如下:
-
首先用字符串的方式来保存大数,这样数字在数学表示上就不会发生变化
-
初始化res,temp来保存中间的计算结果,并将两个字符串转化为数组,以便进行每一位的加法运算
-
将两个数组的对应的位进行相加,两个数相加的结果可能大于10,所以可能要仅为,对10进行取余操作,将结果保存在当前位
-
判断当前位是否大于9,也就是是否会进位,若是则将temp赋值为true,因为在加法运算中,true会自动隐式转化为1,以便于下一次相加
-
重复上述操作,直至计算结束
13. 实现 add(1)(2)(3)
函数柯里化概念: 柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。
1)粗暴版
function add (a) {
return function (b) {return function (c) {return a + b + c;}
}
}
console.log(add(1)(2)(3)); // 6
复制代码
2)柯里化解决方案
-
参数长度固定
var add = function (m) {var temp = function (n) {return add(m + n);}temp.toString = function () {return m;}return temp;
};
console.log(add(3)(4)(5)); // 12
console.log(add(3)(6)(9)(25)); // 43
复制代码
对于add(3)(4)(5),其执行过程如下:
-
先执行add(3),此时m=3,并且返回temp函数;
-
执行temp(4),这个函数内执行add(m+n),n是此次传进来的数值4,m值还是上一步中的3,所以add(m+n)=add(3+4)=add(7),此时m=7,并且返回temp函数
-
执行temp(5),这个函数内执行add(m+n),n是此次传进来的数值5,m值还是上一步中的7,所以add(m+n)=add(7+5)=add(12),此时m=12,并且返回temp函数
-
由于后面没有传入参数,等于返回的temp函数不被执行而是打印,了解JS的朋友都知道对象的toString是修改对象转换字符串的方法,因此代码中temp函数的toString函数return m值,而m值是最后一步执行函数时的值m=12,所以返回值是12。
-
参数长度不固定
function add (...args) {//求和return args.reduce((a, b) => a + b)
}
function currying (fn) {let args = []return function temp (...newArgs) {if (newArgs.length) {args = [...args,...newArgs]return temp} else {let val = fn.apply(this, args)args = [] //保证再次调用时清空return val}}
}
let addCurry = currying(add)
console.log(addCurry(1)(2)(3)(4, 5)()) //15
console.log(addCurry(1)(2)(3, 4, 5)()) //15
console.log(addCurry(1)(2, 3, 4, 5)()) //15
复制代码
14. 实现类数组转化为数组
类数组转换为数组的方法有这样几种:
-
通过 call 调用数组的 slice 方法来实现转换
Array.prototype.slice.call(arrayLike);
复制代码
-
通过 call 调用数组的 splice 方法来实现转换
Array.prototype.splice.call(arrayLike, 0);
复制代码
-
通过 apply 调用数组的 concat 方法来实现转换
Array.prototype.concat.apply([], arrayLike);
复制代码
-
通过 Array.from 方法来实现转换
Array.from(arrayLike);
复制代码
15. 使用 reduce 求和
arr = [1,2,3,4,5,6,7,8,9,10],求和
let arr = [1,2,3,4,5,6,7,8,9,10]
arr.reduce((prev, cur) => { return prev + cur }, 0)
复制代码
arr = [1,2,3,[[4,5],6],7,8,9],求和
let arr = [1,2,3,4,5,6,7,8,9,10]
arr.flat(Infinity).reduce((prev, cur) => { return prev + cur }, 0)
复制代码
arr = [{a:1, b:3}, {a:2, b:3, c:4}, {a:3}],求和
let arr = [{a:9, b:3, c:4}, {a:1, b:3}, {a:3}]
arr.reduce((prev, cur) => {return prev + cur["a"];
}, 0)
复制代码
16. 将js对象转化为树形结构
// 转换前:
source = [{id: 1,pid: 0,name: 'body'}, {id: 2,pid: 1,name: 'title'}, {id: 3,pid: 2,name: 'div'}]
// 转换为:
tree = [{id: 1,pid: 0,name: 'body',children: [{id: 2,pid: 1,name: 'title',children: [{id: 3,pid: 1,name: 'div'}]}}]
复制代码
代码实现:
function jsonToTree(data) {// 初始化结果数组,并判断输入数据的格式let result = []if(!Array.isArray(data)) {return result}// 使用map,将当前对象的id与当前对象对应存储起来let map = {};data.forEach(item => {map[item.id] = item;});// data.forEach(item => {let parent = map[item.pid];if(parent) {(parent.children || (parent.children = [])).push(item);} else {result.push(item);}});return result;
}
复制代码
17. 使用ES5和ES6求函数参数的和
ES5:
function sum() {let sum = 0Array.prototype.forEach.call(arguments, function(item) {sum += item * 1})return sum
}
复制代码
ES6:
function sum(...nums) {let sum = 0nums.forEach(function(item) {sum += item * 1})return sum
}
复制代码
18. 解析 URL Params 为对象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型city: '北京', // 中文需解码enabled: true, // 未指定值得 key 约定为 true
}
*/
复制代码
function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中let paramsObj = {};// 将 params 存到对象中paramsArr.forEach(param => {if (/=/.test(param)) { // 处理有 value 的参数let [key, val] = param.split('='); // 分割 key 和 valueval = decodeURIComponent(val); // 解码val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值paramsObj[key] = [].concat(paramsObj[key], val);} else { // 如果对象没有这个 key,创建 key 并设置值paramsObj[key] = val;}} else { // 处理没有 value 的参数paramsObj[param] = true;}})return paramsObj;
}
复制代码