linqjs - linq to javascript

目录(?)[+]


这里给大家推荐个js实现的linq,项目地址:mihaifm/linq

1. 遍历与展开#

traverseBreadthFirst(广度优先遍历树结构)#

const tree = { 
  value: 1, 
  children: [
    { value: 2, children: [{ value: 4 }] }, 
    { value: 3 }
  ] 
};

from(tree)
  .traverseBreadthFirst(node => node.children)
  .select(node => node.value)
  .toArray(); // [1, 2, 3, 4]

traverseDepthFirst(深度优先遍历树结构)#

from(tree)
  .traverseDepthFirst(node => node.children)
  .select(node => node.value)
  .toArray(); // [1, 2, 4, 3]

flatten(展开嵌套序列)#

from([1, [2, [3]], 4])
  .flatten()
  .toArray(); // [1, 2, 3, 4]

selectMany(展开并映射,类似 flatMap#

from([{ items: [1, 2] }, { items: [3] }])
  .selectMany(x => x.items)
  .toArray(); // [1, 2, 3]

scan(累积遍历,类似 reduce 但返回中间结果)#

from([1, 2, 3])
  .scan((acc, x) => acc + x, 0)
  .toArray(); // [1, 3, 6](每一步的累加结果)

asEnumerable(转换为可枚举对象)#

const lazySeq = from([1, 2, 3]).asEnumerable();
lazySeq.where(x => x > 1).toArray(); // [2, 3]

2. 过滤与筛选#

where(条件过滤)v#

from([1, 2, 3, 4])
  .where(x => x % 2 === 0)
  .toArray(); // [2, 4]

ofType(按类型筛选)#

from([1, "a", 2, "b"])
  .ofType("string")
  .toArray(); // ["a", "b"]

distinct(去重)#

from([1, 2, 2, 3])
  .distinct()
  .toArray(); // [1, 2, 3]

distinctUntilChanged(去重连续重复值)#

from([1, 1, 2, 2, 1])
  .distinctUntilChanged()
  .toArray(); // [1, 2, 1]

except(排除指定集合元素)#

from([1, 2, 3, 4])
  .except([2, 4])
  .toArray(); // [1, 3]

takeWhile(取满足条件的连续元素)#

from([1, 2, 3, 2, 4])
  .takeWhile(x => x < 3)
  .toArray(); // [1, 2]

skipWhile(跳过满足条件的连续元素)#

from([1, 2, 3, 2, 4])
  .skipWhile(x => x < 3)
  .toArray(); // [3, 2, 4]

takeExceptLast(排除最后一个元素)#

from([1, 2, 3])
  .takeExceptLast()
  .toArray(); // [1, 2]

isEmpty(判断序列是否为空)#

from([]).isEmpty(); // true
from([1]).isEmpty(); // false

3. 转换与映射#

select(映射元素)#

from([1, 2])
  .select(x => x * 2)
  .toArray(); // [2, 4]

choose(条件映射并过滤空值)#

from([1, 2, 3])
  .choose(x => x % 2 === 0 ? x * 2 : null)
  .toArray(); // [4]
    • 如果 x 能被2整除(即 x % 2 === 0),则返回 x * 2。例如,2能被2整除,所以返回 2 * 2 = 4
    • 如果 x 不能被2整除,则返回 null。例如,1和3都不能被2整除,所以返回 null

cast(强制类型转换)#

from(["1", "2"])
  .cast(Number)
  .toArray(); // [1, 2]

pairwise(将相邻元素配对)#

from([1, 2, 3])
  .pairwise()
  .toArray(); // [[1, 2], [2, 3]]

zip(合并两个序列为元组)#

from([1, 2])
  .zip(["a", "b"], (a, b) => a + b)
  .toArray(); // ["1a", "2b"]

merge(合并多个序列)#

from([1, 2]).merge([3, 4]).toArray(); // [1, 2, 3, 4]

alternate(交替插入元素)#

from([1, 3])
  .alternate(2, 4)
  .toArray(); // [1, 2, 3, 4]

insert(在指定位置插入元素)#

from([1, 3])
  .insert(1, 2)
  .toArray(); // [1, 2, 3]

reverse(反转序列)#

from([1, 2, 3]).reverse().toArray(); // [3, 2, 1]

shuffle(随机打乱顺序)#

from([1, 2, 3]).shuffle().toArray(); // 随机排列,如 [2, 3, 1]

groupBy(按键分组)#

from([{ id: 1, type: "A" }, { id: 2, type: "A" }, { id: 3, type: "B" }])
  .groupBy(x => x.type)
  .select(g => ({ type: g.key, count: g.count() }))
  .toArray(); // [{ type: "A", count: 2 }, { type: "B", count: 1 }]

partitionBy(按条件分区)#

from([1, 2, 3, 2, 4])
  .partitionBy(x => x % 2 === 0)
  .toArray(); // [[2, 2, 4], [1, 3]]

4. 聚合与计算#

aggregate(自定义累积计算)#

from([1, 2, 3])
  .aggregate((acc, x) => acc + x, 0); // 6

sum(求和)#

from([10, 20]).sum(); // 30

average(求平均值)#

from([10, 20]).average(); // 15

count(计数)#

from([1, 2, 3]).count(); // 3

max / min(最大值/最小值)#

from([5, 1, 3]).max(); // 5  
from([5, 1, 3]).min(); // 1

✔maxBy/minBy`(按属性取最大/最小对象)#

from([{ age: 20 }, { age: 30 }])
  .maxBy(x => x.age); // { age: 30 }

from([{ name: "Bob", score: 80 }, { name: "Alice", score: 90 }])
  .minBy(x => x.score); // { name: "Bob", score: 80 }

all(是否全部满足条件)#

from([2, 4, 6]).all(x => x % 2 === 0); // true

any(是否存在满足条件的元素)#

from([1, 3, 5]).any(x => x % 2 === 0); // false

weightedSample(按权重随机抽样)#

from([{ value: "A", weight: 1 }, { value: "B", weight: 3 }])
  .weightedSample(x => x.weight)
  .take(1); // "B" 出现的概率是 75%

5. 序列操作与组合#

concat(连接两个序列)#

from([1, 2]).concat([3, 4]).toArray(); // [1, 2, 3, 4]

intersect(取交集)#

from([1, 2, 3]).intersect([2, 3, 4]).toArray(); // [2, 3]

union(取并集并去重)#

from([1, 2, 2]).union([3, 2]).toArray(); // [1, 2, 3]

sequenceEqual(判断序列相等)#

from([1, 2, 3]).sequenceEqual([1, 2, 3]); // true
from([1, 2, 3]).sequenceEqual([3, 2, 1]); // false(顺序敏感)

buffer(分批次缓存元素)#

from([1, 2, 3, 4, 5])
  .buffer(2)
  .toArray(); // [[1, 2], [3, 4], [5]]

skip(跳过前 N 个元素)#

from([1, 2, 3, 4]).skip(2).toArray(); // [3, 4]

take(取前 N 个元素)#

from([1, 2, 3, 4]).take(2).toArray(); // [1, 2]

takeFromLast(取最后 N 个元素)#

from([1, 2, 3, 4]).takeFromLast(2).toArray(); // [3, 4]

elementAt / elementAtOrDefault(按索引取元素)#

from([10, 20, 30]).elementAt(1); // 20
from([10, 20, 30]).elementAtOrDefault(5, -1); // -1(索引越界返回默认值)

first / firstOrDefault(取首个元素)#

from([5, 6, 7]).first(); // 5
from([]).firstOrDefault(0); // 0

last / lastOrDefault(取末尾元素)#

from([1, 2, 3]).last(); // 3
from([]).lastOrDefault(-1); // -1

single / singleOrDefault(取唯一元素)#

from([5]).single(); // 5
from([1, 2]).singleOrDefault(-1); // 抛出异常(序列包含多个元素)
from([]).singleOrDefault(-1); // -1

6. 查找与索引#

indexOf(查找元素首次出现的位置)#

from(["a", "b", "c", "b"])
  .indexOf("b"); // 1(第一个 "b" 的索引)

// 从索引 2 开始查找
from([10, 20, 30, 20])
  .indexOf(20, 2); // 3

lastIndexOf(查找元素最后一次出现的位置)#

from(["a", "b", "c", "b"])
  .lastIndexOf("b"); // 3(最后一个 "b" 的索引)

// 限定搜索范围
from([10, 20, 30, 20])
  .lastIndexOf(20, 2); // 1(在索引 0-2 范围内查找)

contains(判断是否包含元素)#

from([1, 2, 3]).contains(2); // true  
from([1, 2, 3]).contains(4); // false

defaultIfEmpty(空序列返回默认值)#

from([])
  .defaultIfEmpty("暂无数据")
  .toArray(); // ["暂无数据"]

from([1, 2])
  .defaultIfEmpty("默认值")
  .toArray(); // [1, 2]

7. 排序与分组#

orderBy / orderByDescending(升序/降序排序)#

// 按数值升序
from([3, 1, 2])
  .orderBy(x => x)
  .toArray(); // [1, 2, 3]

// 按属性降序
from([{ name: "Bob", age: 30 }, { name: "Alice", age: 25 }])
  .orderByDescending(x => x.age)
  .toArray(); // [{ age: 30 }, { age: 25 }]

groupJoin(分组关联两个序列)#

const customers = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" }
];
const orders = [
  { customerId: 1, product: "书" },
  { customerId: 1, product: "笔" },
  { customerId: 2, product: "电脑" }
];

// 按客户关联订单(左连接)
from(customers)
  .groupJoin(
    orders,
    c => c.id,
    o => o.customerId,
    (customer, orders) => ({ ...customer, orders: orders.toArray() })
  )
  .toArray();
/* 输出:
[
  { id: 1, name: "Alice", orders: [{ product: "书" }, { product: "笔" }] },
  { id: 2, name: "Bob", orders: [{ product: "电脑" }] }
]
*/

join(关联两个序列)#

// 内连接示例
from(customers)
  .join(
    orders,
    c => c.id,
    o => o.customerId,
    (c, o) => `${c.name} 购买了 ${o.product}`
  )
  .toArray(); // ["Alice 购买了 书", "Alice 购买了 笔", "Bob 购买了 电脑"]

toLookup(转换为键值查找表)#

from(["apple", "banana", "avocado"])
  .toLookup(fruit => fruit[0]); // 按键首字母分组
/* 结果:
{
  'a': ["apple", "avocado"],
  'b': ["banana"]
}
*/

8. 结果转换与输出#

toArray(转换为数组)#

from("hello")
  .toArray(); // ["h", "e", "l", "l", "o"]

toObject(转换为对象)#

from([["name", "Alice"], ["age", 25]])
  .toObject(); // { name: "Alice", age: 25 }

// 使用键选择器
from([{ id: 1, value: "A" }, { id: 2, value: "B" }])
  .toObject(x => `key_${x.id}`, x => x.value); 
// { key_1: "A", key_2: "B" }

toDictionary(转换为字典)#

from([{ id: 1, name: "A" }, { id: 2, name: "B" }])
  .toDictionary(x => x.id); 
/* 结果:
{
  1: { id: 1, name: "A" },
  2: { id: 2, name: "B" }
}
*/

toJSONString(转换为 JSON 字符串)#

from([1, 2, 3])
  .toJSONString(); // "[1,2,3]"

from({ a: 1, b: 2 })
  .toJSONString(); // "{\"a\":1,\"b\":2}"

toJoinedString(连接为字符串)#

from(["a", "b", "c"])
  .toJoinedString("|"); // "a|b|c"

write / writeLine(输出到控制台或流)#

from([1, 2, 3])
  .write(x => console.log(x)); // 逐行输出 1, 2, 3

from(["hello"])
  .writeLine(x => console.log(x + "!")); // 输出 "hello!"

9. 副作用与调试#

doAction(执行副作用操作)#

在数据流中插入副作用(如日志记录),不影响数据传递:

from([1, 2, 3])
  .doAction(x => console.log(`处理元素: ${x}`)) // 输出每个元素
  .select(x => x * 2)
  .toArray(); // [2, 4, 6]

forEach(遍历执行操作)#

直接遍历元素并执行操作:

from(["a", "b", "c"])
  .forEach(x => console.log(x)); // 依次输出 "a", "b", "c"

trace(跟踪元素并输出日志)#

在特定节点打印元素状态:

from([10, 20])
  .trace("当前值:") // 控制台输出 "当前值: 10", "当前值: 20"
  .toArray();

例子2: doActiontrace 一起使用

enumerable.from(self.programWorkflowDefinitions())
    .doAction(x => console.log("处理元素", x)) // 输出每个元素
    .where(function (x) { return x.name === "HT+LQA" })
    .trace("当前值2:") //在这个位置添加一个trace操作符
    .toArray()

log(记录日志)#

记录整个序列的信息:

from([1, 2, 3])
  .log("序列内容:")
  .toArray(); // 控制台输出 "序列内容: [1, 2, 3]"

catchError(捕获异常)#

捕获异常并返回备用序列:

from([1, 2, 3])
  .select(x => { 
    if (x === 2) throw "错误"; 
    return x; 
  })
  .catchError(err => from([4, 5])) // 捕获错误后返回新序列
  .toArray(); // [1, 4, 5]

finallyAction(最终执行操作)#

无论是否出错,最终执行清理操作:

from([1, 2])
  .finallyAction(() => console.log("处理完成!")) // 总会执行
  .toArray();

10. 延迟执行与缓存#

force(强制立即执行)#

强制触发延迟序列的计算:

const lazySeq = from([1, 2, 3]).where(x => x > 1);
lazySeq.force(); // 立即执行查询,返回 [2, 3]

letBind(绑定中间变量)#

在查询中复用中间结果:

from([1, 2, 3])
  .letBind((seq) => ({
    sum: seq.sum(),
    max: seq.max()
  }))
  .toObject(); // { sum: 6, max: 3 }

share(共享序列避免重复计算)#

避免多次枚举导致的重复计算:

const sharedSeq = from(fetchData).share(); // 共享数据源
sharedSeq.take(2).toArray(); // 第一次计算
sharedSeq.take(2).toArray(); // 直接读取缓存

memoize(缓存结果)#

永久缓存结果:

const memoized = from(fetchExpensiveData).memoize();
memoized.toArray(); // 首次计算并缓存
memoized.toArray(); // 直接读取缓存

11. 杂项与底层操作#

letBind(通过闭包传递变量)#

在闭包中传递变量并复用:

from([1, 2, 3])
  .letBind(context => {
    const factor = 10;
    return context.select(x => x * factor); // 使用闭包变量
  })
  .toArray(); // [10, 20, 30]

write / writeLine(流式写入)#

输出到文件流或控制台:

const fs = require("fs");
const stream = fs.createWriteStream("output.txt");
from(["line1", "line2"])
  .write(line => stream.write(line + "\n")); // 写入文件

from(["hello"]).writeLine(console.log); // 输出 "hello" 并换行

asEnumerable(延迟执行转换)#

将类数组对象转换为可枚举序列(延迟执行):

const arrayLike = { 0: "a", 1: "b", length: 2 };
from(arrayLike)
  .asEnumerable()
  .toArray(); // ["a", "b"]

关键区别说明#

  • intersect vs union

    • intersect 返回两个序列的交集(共同元素)。
    • union 返回两个序列的并集(合并后去重)。
  • elementAt vs elementAtOrDefault

    • elementAt 索引越界时抛出错误。
    • elementAtOrDefault 索引越界时返回默认值。
  • single vs first

    • single 要求序列有且仅有一个元素,否则报错。
    • first 直接取第一个元素(允许序列有多个元素)。
  • groupJoin vs join

    • groupJoin 类似左连接,返回主序列所有元素并关联子序列的匹配项集合。
    • join 类似内连接,仅返回主序列与子序列匹配的组合。
  • toObject vs toDictionary

    • toObject 通常用于键值对列表转对象。
    • toDictionary 更明确地通过键选择器构建字典结构。
  • write vs writeLine

    • write 按需输出(可自定义格式),writeLine 通常自动换行。
  • doAction vs forEach

    • doAction 用于在数据流中插入副作用(不终止流)。
    • forEach 直接遍历并终止流(类似终端操作)。
  • share vs memoize

    • share 仅在单次枚举中共享计算结果(重新枚举会重新计算)。
    • memoize 永久缓存结果(后续枚举直接读取缓存)。
  • asEnumerable 的用途

    • 将非标准可迭代对象(如类数组、生成器)转换为支持 linqJS 操作的序列。

作者:【唐】三三

出处:https://www.cnblogs.com/tangge/p/18683872

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   【唐】三三  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2019-01-21 (二)Knockout 文本与外观绑定
2019-01-21 Knockout案例: 全选
2015-01-21 MVC - 19.Log4net
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示