V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
wm123450405
V2EX  ›  分享创造

JavaScript 数据操作工具包

  •  2
     
  •   wm123450405 ·
    wm123450405 · 2018-03-29 14:54:59 +08:00 · 3046 次点击
    这是一个创建于 2192 天前的主题,其中的信息可能已经有所发展或是发生改变。

    源码:

    https://github.com/wm123450405/linqjs
    https://gitee.com/wm123450405/linqjs (国内)

    简介:

    • 对 JavaScript 中原生数组、对象进行扩展, 提供了一些对数据的操作方法.
    • 包括对数组结构、树形结构、对象结构等数据进行 查询,排序,连接,合并,分组,分段,转换,遍历 等一系列功能.
    • 可以用精炼和易懂的代码实现比较复杂的操作

    案例:

    • 案例 1
    let array = [ 'zhao', 'qian', 'sun', 'li', 'zhou', 'wu', 'zheng', 'wang' ]
    //将上述数组按照字母长度进行排序, 字母长度一致的按照字母顺序倒序排序
    console.log(array.asEnumerable().orderBy(str => str.length).thenByDescending().toArray());
    //结果为
    //wu,li,sun,zhou,zhao,wang,qian,zheng
    
    • 案例 2
    //一些人
    let magnus = { name: "Hedlund, Magnus" }, 
        terry = { name: "Adams, Terry" }, 
        charlotte = { name: "Weiss, Charlotte" };
    //一些宠物
    let barley = { name: "Barley", owner: "Adams, Terry" }, 
        boots = { name: "Boots", owner: "Adams, Terry" }, 
        whiskers = { name: "Whiskers", owner: "Weiss, Charlotte" }, 
        daisy = { name: "Daisy", owner: "Hedlund, Magnus" };
    //一个包含所有 人 的数组
    let people = [ magnus, terry, charlotte ];
    //一个包含所有 宠物 的数组
    let pets = [ barley, boots, whiskers, daisy ];
    
    //这是一个一对多关系的两个数组
    //需要显示出来所有的 人-宠物 的关系信息
    //传统做法是
    for (let p of people) {
        for (let pet of pets) {
            if (pet.owner === p.name) {
                 console.log(`${ p.name } - ${ pet.name }`);
            }
        }
    }
    
    //使用我的工具是
    //思想类似数据库查询(如下语句), 连接两个数组(表), 条件为 person 的 name 与 pet 的 owner 相等
    //select person.name, pet.name from `people` as person join `pets` as pet on person.name = pet.owner
    people.asEnumerable()
        .join(pets, (person, pet) =>`${ person.name} - ${ pet.name}`, 
            person => person.name, 
            pet => pet.owner)
        .forEach(res => console.log(res));
    
    • 案例 3
    //这是一个树形结构
    let tree = {
        value: 'a',
        children: [
            {
                value: 'b',  //此节点为 节点 c 与 节点 f 的最小公共祖先
                children: [
                    { value: 'c' },
                    {
                        value: 'd',
                        children: [
                            { value: 'e' },
                            { value: 'f' },
                            { value: 'g' },
                        ]
                    }
                ]
            },
            { value: 'h', }
        ]
    };
    
    //获取一个树形的中多个节点的最小公共祖先
    let root = tree.asEnumerable(node => node.children, node => node.value);
    console.log(root.lowestAncestor('c', 'f')); //结果是 b
    

    更多用法及案例 希望大家进入文档查看 http://wm123450405.oschina.io/linqjs/#/zh-cn

    望大家多多关注 并给于意见和指导 谢谢

    19 条回复    2018-04-02 16:39:17 +08:00
    wm123450405
        1
    wm123450405  
    OP
       2018-03-29 15:54:33 +08:00
    一开始,在 ES5 的时代,我觉得 js 对数组的操作实在太繁琐了. 可能正是这种繁琐才有了像 underscore 这样优秀的 js 库的出现.

    我本不是做前端出身的,我觉得其他语言对于数组或列表的操作要比 js 好用很多,提供了大量的方法和功能. 于是最早参考的 C#中的功能实现了一些方法.

    后来,ES6 发布并普及开来,js 原生数组也增加了很多好用的功能,但是这还远远不够.

    于是我又翻出了我的代码用 ES6 重写.这一次我又借鉴了另外一些语言中的部分特性,比如 php.我想我后面可能再会添加一些其他语言类似的功能,比如 Java 等.

    后来我在 stackoverflow 中看到很多人对于树形结构的操作提了很多的问题,我像我的工具包应该要可以为他们提供帮助.于是在最新的几个版本中我加入了对树形结构的一些操作.

    我想现在这个工具包应该算比较稳定了. 所以我发上来希望和大家交流交流. 也希望能学到更多的东西.
    codermagefox
        2
    codermagefox  
       2018-03-29 16:22:11 +08:00
    楼主你好,请问我能抄一个自己做实现吗?
    per
        3
    per  
       2018-03-29 16:51:40 +08:00 via iPhone
    @wm123450405 没看代码,不过你这个是在数据类型的 prototype 上修改的吗?
    wm123450405
        4
    wm123450405  
    OP
       2018-03-29 17:01:08 +08:00
    @per 是的 在 prototype 上做了扩展的
    wm123450405
        5
    wm123450405  
    OP
       2018-03-29 17:01:35 +08:00
    @codermagefox 可以啊 我也是造了一些轮子而已
    codermagefox
        6
    codermagefox  
       2018-03-29 17:03:07 +08:00   ❤️ 1
    @wm123450405 #5 准备看完 underscore 再来看你这个,然后自己撸个试试。感谢,已 star
    per
        7
    per  
       2018-03-29 18:04:32 +08:00 via iPhone
    @wm123450405 那我这里有个问题,比如一个数组是由一个类 A 实例化得到的,然后这个 A 也是继承于其他类,那当访问你的这个方法的时候会通过原型链一层层往上找,考虑过开销嘛?
    crs0910
        8
    crs0910  
       2018-03-29 18:17:59 +08:00   ❤️ 2
    wm123450405
        9
    wm123450405  
    OP
       2018-03-29 18:38:15 +08:00 via Android
    @per 不好意思 不是很明白你想表达的意思 你可以举例说明下嘛 我这里只是扩展了 Array 类 用 Array.prototype 的方式
    wm123450405
        10
    wm123450405  
    OP
       2018-03-29 18:40:15 +08:00 via Android
    @crs0910 你是让我看看 mootools 这个库吗? 如果你希望的只是类似 flatMap 这样的展开一个包含数组元素的数组 在我的库里是 selectMany
    zenxds
        11
    zenxds  
       2018-03-29 18:48:18 +08:00   ❤️ 1
    改原生数组的原型后患无穷,ES 标准里的一些方法名那么奇怪就是因为当年 prototype.js 的遗留才不得不改名
    wm123450405
        12
    wm123450405  
    OP
       2018-03-29 18:54:27 +08:00 via Android
    @zenxds 我的库默认只会在 Array 上注册一个 asEnumerable 方法 用来获取一个 IEnumerable 对象进行后续操作 当然也可以开启对 Array 的直接扩展 主要看你有没有用到其他有冲突的库
    per
        13
    per  
       2018-03-29 18:57:01 +08:00   ❤️ 1
    @wm123450405 我表达的不是很清楚,你可以看一下 MDN 上的解释。不过还是给你点个星
    wm123450405
        15
    wm123450405  
    OP
       2018-03-29 20:45:03 +08:00
    @per 感谢你提供的信息. 我想我在编写这个库的时候并没有考虑这一层面的性能问题.
    首先你分享的文章我不是特别的明白 我理解的大概含义 一般的 js 引擎会对已有的对象的属性查找进行优化 会将属性按照某种顺序排列在内存中 并生成一个身份(shape 或 structure ID) 如果修改 prototype 的话 会打乱这种顺序 导致原本可以快速查找属性的功能失效 造成性能损失
    就你一共提出的问题 我想:
    如果说因为原型链比较深导致其查找 property 比较慢的话 我想这个应该不容易避免 因为原型链的深度是受类型的继承关系的影响
    如果是因为修改 prototype 导致 js 引擎在做优化时不能达到最佳性能的话 我目前也没有更好的解决办法
    wm123450405
        16
    wm123450405  
    OP
       2018-03-29 22:28:40 +08:00 via Android
    首先感谢各位的关注 如果大家发现有什么问题或者有什么希望加入的功能 欢迎大家在此回复 或者直接在 github/gitee 上提 issue 给我
    raphaelsoul
        17
    raphaelsoul  
       2018-04-02 09:47:51 +08:00   ❤️ 1
    拒绝修改原生数据类型的库:(
    narcotics
        18
    narcotics  
       2018-04-02 14:30:24 +08:00 via Android   ❤️ 1
    lodash 的做法是用 _.chain 做一层包装,再实现链式操作,最后用 _.value 做"拆箱"

    总之改变原生原型不是个好实践
    wm123450405
        19
    wm123450405  
    OP
       2018-04-02 16:39:17 +08:00
    回复 楼上 2 位
    首先 我的库里目前只对原生对象扩展了 asEnumerable 一个方法. 就是尽量避免冲突
    其次 库也支持 Enumerable(source)的方式获取一个和 source.asEnumerable()一样的结果, 类似 lodash 的 chain. 再使用 toArray 等方法生成最终结果.
    当然 如果大家觉得修改原生原型的做法确实不合适 我想也我会在后面的版本中修改 /删除这种方式
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5436 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 08:43 · PVG 16:43 · LAX 01:43 · JFK 04:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.