最近在做一个面向企业内部生产运输的系统, 生产排程及排车排程算法异常复杂, js 原生容器字典非常简陋, 也缺乏对容器字典进行各种排序过滤反查的手段.

像 java 原生就支持多种集合字典, 不行还有 guava 来凑.

C#的 Linq 就更不用说了.

这几天 js 写的越多就越怀疑人生, 数据处理稍微复杂一点的啥都要自己写工具函数来干, 是不是我水平太菜了, 实际 js 是有简单现成的进行复杂的数据处理的办法的?

具体点吧, 比如有界 /无界+阻塞 /非阻塞+去重 /不去重队列, 有序集合, 按对象指定属性或函数判重的集合, 双向字典, 多 key 字典, 字典集合原生支持 getOrAdd(或 Compute)之类的操作, 字典按某个字段分组(类似 SQL 里的 group by), 集合依次按对象多个属性进行排序.

对 Java 不大了解,可以详细讲一下“容器字典”、“集合字典”是什么概念嘛?

处理一组数据的话,通常 JS 里可以使用 Array 、Map 、Set ,特殊需求下可以自己构造数据结构和自建迭代器。

建议不要用一种技术栈的思维硬套在另一种技术栈上,比如你完全用 Java 的思想来写 C#也一定很难受,既然要用 JS 就最好按照 JS 的思维来设计程序。

所以可以把具体问题发出来,V 站里不缺精通 JS 的人,大家可以帮你看看究竟是 JS 不合适这种需求还是有你不了解的更好的方案。

写过一些 JS ,我都是手写,但是专业写 JS 的都会用 lodash 类似的库

lodash.com/

js 作为编程语言来说本身就是有残缺的,因为他原来就是个脚本语言,运行环境也仅仅是在浏览器环境。

我感觉 javascript 处理数据最糟糕的是处理二进制数据,
关键不同的第三方库都是分别封装自己专用的字节数组,
有些情况实在没办法甚至只能序列化再反序列化来传递参数,

不过我不是专业 js 的,不怎么确定,

js 标准库是残废的 要自己引数据结构的包

但是 js 生态繁荣啊,这么多年修修修补补,写后台逻辑还是够的,建议你多去看看社区现有的轮子,比自己硬干好。

比如有界 /无界队列, 阻塞 /非阻塞队列, 有序集合, 按对象指定属性判重的集合, 双向字典, 多 key 字典, 字典集合原生支持 getOrAdd(或 Compute)之类的操作, 字典按某个字段分组(类似 SQL 里的 group by), 集合依次按对象多个属性进行排序,...

能伸手问下, 有哪些现成的轮子吗

JS/ES 是个一直在快速迭代发展的语言,到现在依然每个月都有新的草案被提出或正式被引擎支持,其实已经有相当多的新特性可以给开发提供各种便利了,只不过很多人不知道罢了。

如果对 JS 的印象还停留在在几年之前,已然今非昔比。

#8 楼上已经有朋友说了 lodash ,另外推荐一个 ramda 。你在 7 楼提到的 groupBy 这种操作这些工具库是有的。队列与栈在 JS 里都是数组的原生操作就支持,不过要实现你说的有界 /无界种种限制,可能要借助第三方库,善用 npm 即可。

npmjs.org github.com 直接搜你要的东西

看了楼上的回答,nodejs 在国内不火倒是有原因的。

确实如此,我在知乎上提到 nodejs 的语言的一些优势,一堆人来喷我。。。他们摆出的一些观念和语言特性,基本上还停留在 0.X 的版本

如果还是使用的的 ES5 的话,那么处理数据确实比较麻烦,建议使用 ES2020+

比如分组,自带的就有 Array.prototype.group() (可能需要 polyfill ),还有 Map Set 这些完全够用了
再不够,直接用 lodash

沒錯,後台還是用 python 更方便

js 的最大缺陷大概就是没有(支持直接共享内存的)原生线程吧,worker 的线程其实也可以用一些同步原语(包括原子操作和锁定语义),SharedArrayBuffer 也不是没有——但是只能操作二进制数据,不能直接用来传递对象——因而进行相关封装的就比较少(所谓阻塞队列,基本也是在多线程语境下才有意义吧),这也是为什么明明 wasm 性能不一定比 js 好,但是仍然有很多用到多线程处理的库采用 wasm 进行(
不过对于简单的多线程处理,采用 postMessage 的方法也不是完全不能接受,太重的计算就不太适合了

你说的这些和 ES 这门语言没啥关系. ES 的标准库太弱了, 前端看上去如此繁荣的主要原因就是因为标准库太弱, 而 JS 要做的事情又越来越多, 因此大家不停的造出来各种轮子, 然后有了 npm 这种辣鸡, 然后不停的玩废掉一个个框架, 再来创建一个新的. 核心原因就是标准库太弱了, 随便实现点什么功能都要引用一大堆第三方 library, 而且很多 Library 就几行代码, 你根本不知道整个 npm 按照的依赖都是些什么鬼东西.

js 毕竟客户端语言,不应该设计出很重的标准库,用 js 就要习惯于去找社区实现的轮子

有界 /无界:数据流 Stream 吗?用的不太多,其他人可以补充。
阻塞 /非阻塞:Promise | async await
去重 /不去重队列:去重可用 Set ,不去重 Array Map 都可
有序集合:Array Map 自带顺序
按对象指定属性或函数判重的集合:ArrayLike.filter()
双向字典:用两个 set
多 key 字典:add, search 用 set 就可以,delete 可以用双向字典实现 (这个可能稍微复杂一点)
字典集合原生支持 getOrAdd(或 Compute)之类的操作:Array.map()
字典按某个字段分组(类似 SQL 里的 group by):仅查询时候用 ArrayLike.filter() 就可以。如果要优化查询速度直接 Set 包 Array 就可以 new Set
集合依次按对象多个属性进行排序:Array.sort()

你的这些基本上都可以用 ES6 的数据结构加上他们的方法快速实现掉,甚至都没有用上 lodash 。如果是语法角度的话,JS (ES6) TS 是非常 ducktyping 的,不需要先定义是什么数据结构而是通过函数链式组合来实现功能。

JS 圈连 left padding 这种一句话的事都要专门写一个包。据说凡是能用 JS 重写的都会用 JS 重写(另一个有此殊荣的语言是 Rust )。你想做的事,社区都有包,只是包的质量不一定靠谱罢了。

#20 是指 String.prototype.padStart()嘛?

你说的这些 js 基础库确实没有啥支持的,正常一下子也用不到那么多。
不过基本的排序过滤反查其实都是有的,稍微多一点代码也都能实现。

js 中基础的数据结构是不缺的,数组其实就是变长数组,也支持 push()、pop()、shift(),基本就是有队列、栈的功能。
ES6 有 Set 、Map ,而且支持按插入顺序遍历,甚至对象本身其实也能做一个 Map 。

go 不香吗

这个出现的也有些晚了,在 ES 2017 里面出现的(常说的 ES 6 指 ES 2015 ),当然,现在大部分情况下都是可用的了 😆

历史上著名的 azer left-pad 包血案……事后还有人做了一个段子包 is-thirteen 来调侃 JS 圈封包太小太细的现状

去重 _.uniq, _.unionBy
排序 _.sortBy, _.orderBy
分组 _.groupBy

lodash.com/docs/4.17.15

uniqBy

你仿佛在搞笑,go 也配和 js 比,还是在数据操作方面,就 go 这种啥都没有的语言,都不够给其他语言提鞋,垃圾中的垃圾

很多前端不知道这些的,java C#里标准容器很多宝库的 js 是没有的

我 js 和 Java 都写,C#不写。个人也觉得 js 写后台不好,但也没那么差,主要是要:扬长避短。

js 写起来比较舒服的点:
1 、函数式支持。数组可以直接使用 map 、reduce 、filter ,Java 这边就得先弄成 Stream 搞流式编程。
2 、如果要创建一个对象,不必特定搞一个类。直接双花括号就可以。
3 、语法糖。js 的语法糖很多,比如正则表达式、模板字符串、解构赋值等,处理 JSON 更是极其方便。
4 、没有 Java 那个检查型异常,写起来方便很多。
5 、异步支持。js 有 promise 、async/await 。由于自身单线程的特性,对异步的支持非常好。

但缺点:
1 、不严谨。js 本身有很多设计缺陷,比如 falsy 、undefined 还有各种离谱的隐式转换等等,不熟悉的话容易踩坑,考验你和同事的水平。
2 、性能,js 主要考虑功能,很少考虑实现。比如 ES6 的 Set 就是直接 const set=new Set()。数组当队列也是,能用就行。
3 、单线程事件循环模型,比如你的阻塞队列就是没有的。
能拿来写后台的一个很大原因是不少后台都是 IO 密集型而非计算密集型。而 JS 是把网络 IO 交给底层去管,所以轻松。要是计算密集型估计就蛋疼了。
4 、工具差。官方模块化基本是 ES6 的事了,导致要考虑 CommonJS 、ES Module 、AMD 啥的。npm 设计也是稀烂,包管理器都能吵起来。
5 、生态差,缺乏最佳实践。隔壁 Spring 开发 web 后台,大体都给你定好了,直接用就行。

另外非常赞同#1 的观点,不要用 Java 的思想写 JS ,比如:
1 、Java 有类。比如 toString()、hashCode()、equals()方法你可以直接改类。但 JS 如果你用花括号定义的对象其实是直接来自 Object 的,个人觉得改 Object.prototype 应该不是个好主意。
2 、Java 有类,有注解,有反射机制。比如 JPA ORM 就靠反射获取信息,但 JS 的肯定就不是这么做了。
3 、 注解。既有注解,又是线程相关的模型。
4 、Java 强制你用面向对象的语法写,但 js 没有。

看了题主的附言,也能够理解题主的困惑,JS 目前不包含很多现成的复杂数据结构,但是繁荣的技术栈生态使得总能够找到第三方的实现,而且可以找到多家实现进行 PK ,如果一种实现被大多开发者接受,那么也有可能以草案的形式逐渐成为 JS/ES 标准的一部分,算是某种市场化发展理念吧。

所以客观来说,题主附言里的大多东西 JS 标准 API 里是没有的,大量涉及这些数据结构和算法的场景就需要第三方库或自建库来实现了。

我做过 7 年的 JS 后端开发,但确实日常业务中涉及队列的情况比较少,要么就是分布式微服务架构下使用 Redis 、RabbitMQ 之类的消息队列中间件,要么就是自己简单手写个队列(比如流控、时序和削峰填谷),所以暂时也无法推荐什么好用的库,我只说一些自己实现的思路吧。

单纯的先进先出的话,Array 有现成的 push 、pop 、shift 、unshift 等方法可以用,取决于你是想按索引正序还是倒序排队。
有界无界这个没有原生 API ,如果需要的话我通常是把 Array 封装成一个对象,然后在 add 的时候自己加个数量判断。
JS 使用 promise 及其语法糖控制异步过程,阻塞可以在 add 或 remove 的时候判断队列状态,需要阻塞就暂时不 resolve promise 。
去重可以内置 Set 或 Map ,也可以在封装的 add 方法中自己定义复杂重复判定过程。
有序集合可以用 Map 或 Object ,无序集合可以用 Array 、Set 。
双向字典和多 Key 字典可以用多个个 Map 或 Object 封装一下。
getOrAdd 可以在 Map 、Object 、Set 基础上封装。
分组和排序可以用 lodash 之类的工具库,sort 很多时候手写个 callback 也挺方便的。

如果频繁使用这些数据结构的话,可以自己写个 library ,然后在各个项目中引用。

JS 的内置库确实没 JAVA 和 C# 丰富,不过解释型的语言倒不拘泥这块,用是没问题的,正常开发也感受不到相比其他语言有什么明显缺陷。复杂的数据处理之类的,应该去引用第三方的库,一般都有很成熟、热门且好用的开源库给你选。
数据集合操作我推荐引用 ramba.js ,FP 爽呀
另外,JS 和 JAVA 、C# 思想还是有些不同,有些东西不能类比

github.com/ecrmnn/collect.js 这个 collect.js 觉得 挺好用的

说得好,我选择前端用 js ,后端用其他语言。

总感觉题主是没有人带着做 JS 项目,自己撸出的问题。。

说的 2016 年 leftpad 删库事件吧,多少年前的老黄历了

错误的提问方式:
求推荐 javascript 处理集合的基础库。
回复:2 收藏:16

正确的提问方式:
js 先天残缺,没有处理集合基础库。
回复:100+ 收藏:0

go 也很缺这些,个人觉得对开发效率和 bug 率都挺有影响的

然而评论里很多 提到 lodash ramda 库, 这些人估计没搞过数据结构
题主提到的 js 缺少的数据结构 这 2 个库都搞不定。

lodash 了解一下,js 基础库是简漏了点,但奈不住包多 屁大个方法都有对应的包

有道理 哈哈哈

我觉得主要还是人的问题

前后端都用 js ,有一些非常方便的特性:

  1. 前后端可共用一些基础业务类型 /工具,代码一致性高
  2. 前后端通讯不需要定义 http 接口,实现一个简单的 rpc 就可以像调用方法一样调用后端 API ,并且类型安全
  3. 设施准备良好后,项目启动快,改动前后端代码都触发浏览器刷新,生产力之选

#19 的 的说完全能搞定呀。

觉着是楼主自己不知道

开惯了特斯拉在开三缸手动挡,当然会觉得先天残缺

#20L 我仿佛听到了什么笑话 padStart, padEnd 不能用吗?

#36L 别人删个库有什么大惊小怪的, 自己项目中不锁版本吗? 没有 nexus repository 吗?

在现阶段,我国社会的主要矛盾是人民日益增长的物质文化需要同落后的社会生产力之间的矛盾。

首先需要说明的是 js 有很多版本,其次要说明的是 js 本身只是一个脚本语言,从出生到现在用的比较多的是在前端领域。在后端的应用也是在 node 出来以后。你提的一堆数据结构,或者业务场景即便在 java 中也不是一开始就有的吧。你拉着一个英国人问他你是不是天生在说中文上有缺陷,这是不是有点扯,一切都是在进步的。一个复杂的系统要想追求机制的性能,很难一套语言一把梭。并且类似于 java94 年出来,到现在生态已经非常繁荣,js 搞后端也要从 node 出来的 09 年算,并且生产应用规模还不是很大。总之这么一个提问非常没有意义。虽然 java 又臭又长,但是最好还是能把 js 慢慢改成 ts 。

js 也有 linq 插件可以用

就算用 js 写好了 后期维护你自己都看不懂当初的代码。js 只适合拿来写页面

js-sdsl 一个参考 C++ STL 实现的 JavaScript 标准数据结构库,你是说这种数据结构嘛?

每种语言设计之初都有明确的方向和目的
js 被设计成浏览器里运行的语言,它的特性严重依赖各大浏览器厂商的支持程度,而且各家实现方案还不一样
曾经浏览器兼容性问题频频出现在前端面试中,就很搞笑
即便如今 NodeJS 流行开来,也是得益于 JS 开发者多,门槛低,底下用 C*糊了个 V8 和 libuv 获得了不错的性能。
JS 的特性也越来越多,可惜大部分用不到,专业的场景也有比它更好的语言
就像我花 50W 改装一台思域 Type R ,看起来不错,0 百加速甚至媲美超跑,但是呢,上了赛道被 911 吊打,去夜店被兰博基尼吊打,只有红绿灯起步的瞬间能让大家另眼相看

js 有类型之王的 typescript ,不存在看不懂

是的 还有楼主附录的那些,都是 js 缺少的

好像记得 d3.js 作者 还写了个 支持不关注 key 大小写的 map 结构的 js 库 具体忘记了

害,日子不还得过吗
咱 leetcode 周赛都拿 js 来写,一个 heap 我在那糊了 20 分钟
别人引个库两行完事了 😭

一时写一时爽,写多了就是火葬场. 任何弱类型动态语言都"不太适"合写"复杂业务"的后台.

我做过很多类似 lz 这样的需求。首先拿 js 写这样的重计算需求是不太合适的,一是数据结构孱弱,二是缺乏这类算法的轮子。

常见的做法是用 Cpp 或者 py 配合 developers.google.com/optimization
这样的轮子搓一个封装好的函数出来。

然后用 RPC 或者消息队列这样的语言中性的方法暴露接口出来,取决于是需要同步或者异步调用。

前端再调用这样的服务。

可它真的包多...
他是世界上包最多的...

歪个楼,请问生产排程及排车排程算法有啥资料可以看吗,一直挺好奇这个实现的

工业的都偏保守, 没有开源的, 都是各家厂商自己在摸索

#37 发帖还是得标题党一下😂,不然都没什么人回复

JS 没有太多标准库啊。很多东西你不是搞前端的,用后端思想去写就会觉得特别难受。很多后端有的 JS 自身没有。而且 JS 还要考虑国产壳牌浏览器的兼容。。。过于新的语法,你还要引入兼容库。

nodejs 不太清楚,毕竟我没深入写过。

我只能说,你差不多快看到本质了。

如果学编程的人够早,就知道以前的语言是什么样,javascrip 刚出来的时候是什么样。

用 C++的人喜欢说一句话,一个东西一旦好用,那么很容易被滥用,很多语言的维护团队都在极力发扬好用的一面,克服滥用的一面。

JS 派系除外。

我不止一次被 js 开源项目那恐怖的依赖关系恶心到,如果你有幸遇到报错,可以看看究竟抛出了多少东西(我基本上像是看到了一个宇宙级 Exception )。

至于后面的各种封装、库、什么社区、什么前人的轮子,我的理解是,当你造汽车时有一百个轮子可以选,就基本相当于没得选,而且选个轮子耗时,自己写一个轮子也耗时。

于是他们决定用轮子自动造轮子,真的是牛逼至极。

node.js ?

2015 年就已经实现很多常用的操作

针对 Array 的操作 filter\map\every\some , 以及 set 、weakset 、map 、weakmap 相关,都相当好用

在 es6 标准出来之前,相当多应用使用 underscorejs 来执行上诉功能