-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # 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.
|
|
邮箱:15168273390@163.com
|
签名由 网易邮箱大师 定制
在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.
|
|
邮箱:15168273390@163.com
|
签名由 网易邮箱大师 定制
在2018年04月26日 18:50,Func. 写道:
博主你好,我看到“前端大牛爱好者”最新期是你的关于 this 的博文,不知道有没有经过你同意,特此跟你告知一声。
| |
Func.
|
|
邮箱:15168273390@163.com
|
签名由 网易邮箱大师 定制
在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.
|
那个公众号很水的,转载了很多别人的文章,作者是某一培训机构的招生老师,等着你上钩呢??????,总之,没多少是原创的文章
获取 Outlook for Android<https://aka.ms/ghei36>
…________________________________
From: King Muji <notifications@github.com>
Sent: Thursday, April 26, 2018 6:51:25 PM
To: mqyqingfeng/Blog
Cc: Subscribed
Subject: Re: [mqyqingfeng/Blog] JavaScript专题之数组扁平化 (#36)
博主你好,我看到“前端大牛爱好者”最新期是你的关于 this 的博文,不知道有没有经过你同意,特此跟你告知一声。
| |
Func.
|
|
邮箱:15168273390@163.com
|
签名由 网易邮箱大师 定制
在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.
―
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub<https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.heygears.com%2Fmqyqingfeng%2FBlog%2Fissues%2F36%23issuecomment-384596737&data=02%7C01%7C%7C4d5e4744d4bb4e1e94c708d5ab63a8b3%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636603366881442453&sdata=k9mfH9JOb1%2Fz4gSkNOxZB53JsOaZTDKicu4iVi%2BG%2FPc%3D&reserved=0>, or mute the thread<https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.heygears.com%2Fnotifications%2Funsubscribe-auth%2FAR4207OycI7_ypWBO5tIoSBwy-ogl6cNks5tsaatgaJpZM4Oe52j&data=02%7C01%7C%7C4d5e4744d4bb4e1e94c708d5ab63a8b3%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636603366881442453&sdata=miIRaQrwYKI69bVt1qp7LFBrkGnNbCc7%2BTy7IfkOXC0%3D&reserved=0>.
|
@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: