-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript专题之数组扁平化 #36
Comments
日常观光+学习 |
学习了 |
学习了,之后应该可以用到 |
@sulingLiang 扁平化不改变原数据啊,你的结果也显示了,string型的变成了数字都 |
@HuangQiii 感谢回答~ (๑•̀ㅂ•́)و✧ |
楼主你好,我发现你的 flatten() 有点问题: 先看 underscore 的: const _ = require('underscore');
console.log(_.flatten([1, [2, [3, 4]], 5], false)); // [ 1, 2, 3, 4, 5 ]
console.log(_.flatten([1, [2, [3, 4]], 5], true)); // [ 1, 2, [ 3, 4 ], 5 ] 而楼主你的 flatten() 函数的表现如下: /**
* 数组扁平化
* @param {Array} input 要处理的数组
* @param {boolean} shallow 是否只扁平一层
* @param {boolean} strict 是否严格处理元素,下面有解释
* @param {Array} output 这是为了方便递归而传递的参数
* 源码地址:https://github.com/jashkenas/underscore/blob/master/underscore.js#L528
*/
function flatten(input, shallow, strict, output) {
// 递归使用的时候会用到output
output = output || [];
var idx = output.length;
for (var i = 0, len = input.length; i < len; i++) {
var value = input[i];
// 如果是数组,就进行处理
if (Array.isArray(value)) {
// 如果是只扁平一层,遍历该数组,依此填入 output
if (shallow) {
var j = 0,
len = value.length;
while (j < len) output[idx++] = value[j++];
}
// 如果是全部扁平就递归,传入已经处理的 output,递归中接着处理 output
else {
flatten(value, shallow, strict, output);
idx = output.length;
}
}
// 不是数组,根据 strict 的值判断是跳过不处理还是放入 output
else if (!strict) {
output[idx++] = value;
}
}
return output;
}
console.log(flatten([1, [2, [3, 4]], 5], false, false)); // [ 1, 2, 3, 4, 5 ]
console.log(flatten([1, [2, [3, 4]], 5], true, false)); // [ 1, 2, [ 3, 4 ] ] 这里与 underscore 不一致 Debug 发现,楼主的 var j = 0,
len = value.length; // 改为 length = value.length |
@swpuLeo 感谢指出哈 (๑•̀ㅂ•́)و✧ 确实有问题,我自己测试了一下,除了改成 var length = value.length 之外,while (j < len) output[idx++] = value[j++]; 这里的 len 也需要改成 length |
数组扁平化还可以这么写,不过初学者理解不了。
|
@mqyqingfeng 看到dalao用concat和扩展运算符的时候愣了一下,几乎完全这个方法的印象,甚至还是去查了文档才知道 |
@thereisnowinter dalao 666~萌新学到了新的一手 apply和bind连用,同时绑定调用的方法和调用的主体,只留下一个参数的位置 |
@thereisnowinter 666~ 为你打 call~ 稍微解释一下: Function.apply.bind([].concat, [])
// 相当于
function(arg) {
return Function.apply.call([].concat, [], arg)
}
// 相当于
function(arg) {
return [].concat.apply([], arg)
}
// 相当于
// 这里错了
function(arg) {
return [].concat(arg)
}
// 应该是
function(arg) {
return [].concat(...arg)
} |
@Tan90Qian 处理数据或者需要返回一个新数组的时候,会用到 concat |
@mqyqingfeng 那基本就是在不能使用es6的情况下使用咯?否则“扩展操作符”+数组直接量的创建方式基本可以替代它的功能。然后就是类似数组展平这样,仅靠一个concat或者扩展操作符无法完成的功能。 PS:刚写的内容有2个问题,
return function(arg) {
return [].concat(...arg)
} 这也和博主大大在正文中的版本一致。 |
@Tan90Qian 关于这两个问题:
var flatten = Function.apply.bind([].concat, [])
// 相当于
var flatten = function(arg) {
return Function.apply.call([].concat, [], arg)
} 正好就是对应的,不需要 return function 呀
|
@mqyqingfeng 第一条是我弄错了。不过第二条,确实是需要展开的,因为MDN对concat方法的描述: [].concat([1,[2,[3,4]]]) // [1,[2,[3,4]]]
[].concat(...[1,[2,[3,4]]])
// 相当于
[].concat(1,[2,[3,4]]) // [1,2,[3,4]] |
@Tan90Qian 啊,是的,感谢指出~ |
博主你好,我看到“前端大牛爱好者”最新期是你的关于 this 的博文,不知道有没有经过你同意,特此跟你告知一声。
| |
Func.
|
|
邮箱:[email protected]
|
签名由 网易邮箱大师 定制
在2018年04月25日 21:45,冴羽 写道:
@Tan90Qian 啊,是的,感谢指出~
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
|
补充一下,是微信公众号
| |
Func.
|
|
邮箱:[email protected]
|
签名由 网易邮箱大师 定制
在2018年04月26日 18:50,Func. 写道:
博主你好,我看到“前端大牛爱好者”最新期是你的关于 this 的博文,不知道有没有经过你同意,特此跟你告知一声。
| |
Func.
|
|
邮箱:[email protected]
|
签名由 网易邮箱大师 定制
在2018年04月25日 21:45,冴羽 写道:
@Tan90Qian 啊,是的,感谢指出~
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
|
@Fiv5 感谢告知~ 我并不知道这件事情,请教大家这种情况该怎么处理呢? |
@mqyqingfeng 微信公众号文章的话直接投诉就可以吧,有一个“未经授权的文章”选项 |
/* 在javascript权威指南的第六版,p159*/ function union(o,p){return extend(extend({},o),p);} |
楼主您的设计中第一个使用for循环的设计中我在实验这样一个 |
楼主,有一个小疑问,就是方法1中将push变成concat为什么就得不到想要的结果了 function flatten(arr) { console.log(flatten(arr)) |
额 几个月前看没觉得什么 最近突然遇到扁平化才知道不用递归写出扁平化牛P了哈 |
最近ES10标准已经支持扁平化了!Array.flat(),以后也不用写这样的方法啦! |
|
感觉又进步一点点了 |
递归数组后,output会变啊,不变idx,后面再插入数组项不就变成替换以后的了吗,我不明白的是这里怎么不直接用push呢 |
递归时output会变,但不是函数里面会重新 定义var idx = output.length 吗 这和 递归函数之后的那句代码idx = output.length; 并没有关系啊 |
函数内的idx是最新的没错,但是外部的还是原来的,递归结束后肯定要再获取一下 |
console.log([].concat(...arr)) 这里用到的是函数的 rest 参数,大佬说成是数组的扩展运算符了 |
关于 flatten = Function.apply.bind([].concat, [])的理解,想了半天总算理解了。。。 |
|
leecode上看到的骚操作,感觉这个效率应该很高 |
output[idx++] = value[j++];这种出来的数组下标是不规律的,为啥不直接用push |
const flatten = (arr, shallow, strict) => {
return arr.reduce((now, next) => {
if (!shallow) {
next = Array.isArray(next) ? flatten(next, shallow, strict) : next
}
if (strict && !Array.isArray(next)) {
next = []
}
return now.concat(next)
}, [])
} 稍微简化了一下 |
学习了 |
+1 |
解构运算原理来打平数组 function* flatten(arr) {
for (const item of arr) {
if(Array.isArray(item)){
yield* flatten(item)
} else {
yield item
}
}
}
var arr = [1, 2, [3, 4, [5, 6]]];
const flattened = [...flatten(arr)]; |
可以修改原数组的扁平化 // splice 修改原数组
function flattern_v3(arr, depth) {
if(depth < 1) return arr;
let i, len;
for(i=0, len=arr.length; i < len; i++) {
const item = arr[i];
if(Array.isArray(item) && depth) {
arr.splice(i, 1, ...item);
len += item.length - 1;
depth--;
}
}
} |
_.difference([1, 2, 3, 4, 5], [5, 2, 10], [4], 3);
=> [1, 3] 这个示例错误,正确的是 _.difference([1, 2, 3, 4, 5], [5, 2, 10], [4], 3);
=> [1, 3, 10] |
@bosens-China |
可以直接使用数组的.flat()方法,在括号内传入提取嵌套数组的结构深度就行了,MDN:Array.prototype.flat() |
使用 Generator 函数实现数组扁平化: function* flat(arr){
if(Array.isArray(arr)){
for(const item of arr){
yield* flat(item)
}
}else{
yield arr
}
}
// 测试
let flatted = [ ...flat([1,2,[3,4,[5,6]]]) ]
console.log(flatted) // [1,2,3,4,5,6] |
|
你好,我已经收到你的来信!
|
扁平化
数组的扁平化,就是将一个嵌套多层的数组 array (嵌套可以是任何层数)转换为只有一层的数组。
举个例子,假设有个名为 flatten 的函数可以做到数组扁平化,效果就会如下:
知道了效果是什么样的了,我们可以去尝试着写这个 flatten 函数了
递归
我们最一开始能想到的莫过于循环数组元素,如果还是一个数组,就递归调用该方法:
toString
如果数组的元素都是数字,那么我们可以考虑使用 toString 方法,因为:
调用 toString 方法,返回了一个逗号分隔的扁平的字符串,这时候我们再 split,然后转成数字不就可以实现扁平化了吗?
然而这种方法使用的场景却非常有限,如果数组是 [1, '1', 2, '2'] 的话,这种方法就会产生错误的结果。
reduce
既然是对数组进行处理,最终返回一个值,我们就可以考虑使用 reduce 来简化代码:
...
ES6 增加了扩展运算符,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:
我们用这种方法只可以扁平一层,但是顺着这个方法一直思考,我们可以写出这样的方法:
undercore
那么如何写一个抽象的扁平函数,来方便我们的开发呢,所有又到了我们抄袭 underscore 的时候了~
在这里直接给出源码和注释,但是要注意,这里的 flatten 函数并不是最终的 _.flatten,为了方便多个 API 进行调用,这里对扁平进行了更多的配置。
解释下 strict,在代码里我们可以看出,当遍历数组元素时,如果元素不是数组,就会对 strict 取反的结果进行判断,如果设置 strict 为 true,就会跳过不进行任何处理,这意味着可以过滤非数组的元素,举个例子:
那么设置 strict 到底有什么用呢?不急,我们先看下 shallow 和 strct 各种值对应的结果:
我们看看 underscore 中哪些方法调用了 flatten 这个基本函数:
_.flatten
首先就是 _.flatten:
在正常的扁平中,我们并不需要去掉非数组元素。
_.union
接下来是 _.union:
该函数传入多个数组,然后返回传入的数组的并集,
举个例子:
如果传入的参数并不是数组,就会将该参数跳过:
为了实现这个效果,我们可以将传入的所有数组扁平化,然后去重,因为只能传入数组,这时候我们直接设置 strict 为 true,就可以跳过传入的非数组的元素。
_.difference
是不是感觉折腾 strict 有点用处了,我们再看一个 _.difference:
语法为:
效果是取出来自 array 数组,并且不存在于多个 other 数组的元素。跟 _.union 一样,都会排除掉不是数组的元素。
举个例子:
实现方法也很简单,扁平 others 的数组,筛选出 array 中不在扁平化数组中的值:
注意,以上实现的细节并不是完全按照 underscore,具体细节的实现感兴趣可以查看源码。
专题系列
JavaScript专题系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript专题系列预计写二十篇左右,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特点是研(chao)究(xi) underscore 和 jQuery 的实现方式。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: