Fluent Streams:丰富的可迭代操作库

想象一个图书馆:

  • 与 Ramda 类似,提供了许多有用的函数用于可迭代对象操作
  • 读起来很熟悉,就像标准数组迭代方法一样
  • 不会尝试取代 Array、Set 或迭代协议等 JavaScript 内置函数
  • 与 Java Streams 类似,包含 Optional 以明确区分“无值”和“未定义为值”
  • 捆绑尺寸紧凑
  • 我找不到一个可以满足所有这些需求的库,因此我创建了自己的小型库 — fluent-streams。

    说话没多大用处。给我看代码

    文本中出现的前 3 个单词

    const words = ['Lorem', 'ipsum', /* ... */]
    
    stream(words)
      .groupBy(word => word.toLowerCase())
      .map(([w, {length}]) => [w, length])
      .sortBy(([, length])  => -length)
      .take(3)
      .toArray()
    
    // => ['ut', 3], ['in', 3], ['dolor', 2]

    互质整数

    // Endless stream of 2..999 integers
    const randomInts = continually(() => 
      2 + Math.floor(Math.random() * 998)
    )
    
    randomInts
      .zip(randomInts)
      .filter(([a, b]) => gcd(a, b) === 1)
      .distinctBy(([a, b]) => a < b ? `${a},${b}` : `${b},${a}`)
      .take(10)
    
    // => [804, 835], [589, 642], [96, 145], ...
  • 如图所示,流可以是无限的,但你可以使用 take(n) 方法将它们限制为前 n 个项目
  • 流可以多次重复使用,甚至可以并行使用。这是因为流是无状态的,只存储对输入的引用。只有当流生成迭代器时才会创建状态。
  • 生成一副牌

    const deck = streamOf('♠', '♥', '♣', '♦')
      .flatMap(suit =>
        streamOf(
          'A',
          ...range(2, 11),
          'J',
          'Q',
          'K'
        ).map(rank => `${rank}${suit}`)
      )
    
    // => 'A♠', '2♠', '3♠', ...

    玩德州扑克!

    const playersNum = 2
    
    const [flop, turn, river, ...hands] = deck
      .takeRandom(3 + 1 + 1 + playersNum * 2)
      .splitWhen((_l, _r, i) =>
        i === 2             // flop
        || i === 3          // turn
        || i >= 4           // river
          && i % 2 === 0    // ...players' hands
      )
    
    // flop = ['3♦', '9♣', 'J♦']
    // turn = ['4♣']
    // river = ['7♦']
    // hands = ['J♠', 'Q♥'], ['10♠', '8♥']

    第一个玩家在翻牌时有一对 J,而第二个玩家在河牌时拿到顺子。谁会赢?

    这一定很便宜

    以上所有内容都只能通过原生数据结构实现。但是,使用 Fluent Streams 编写的代码读起来更顺畅。虽然让代码更具可读性是一个完全合理的目标,但实现这一目标的成本在认知负荷、包大小和性能方面应该很低。

    Fluent Streams 正是如此!原因如下:

  • 无需学习:API 感觉很熟悉,类似于标准数组方法。添加和删除都很容易。
  • 无需重新发明:该库不会创建新的数据结构或协议 - 它建立在 JavaScript 已经强大的功能之上。
  • 最小的包影响:压缩后仅约 9 kB,非常轻量。如果您的项目已包含 React 及其附属库(大小达数百 KB),则这一增加几乎不会引人注意。
  • 惰性处理:该库以惰性方式处理项目,通过避免不必要的中间数据复制,可以减少内存使用并提高长管道中的效率。
  • 注意事项

    该库在交付时**未转译**为 ES5。做出这一决定是出于保持较小包大小的愿望,这通过利用 ES6+ 功能来实现,这些功能支持使用非常简洁的代码进行迭代 - 最值得注意的是生成器。但是,仅使用广泛支持的语言功能。

    如果您仍在转译为 ES5,您可以通过自行转译并添加 polyfill 来使用该库。但请注意,这会增加包大小,因此不建议这样做。相反,这可能是重新审视您的构建配置并采用现代 JavaScript 功能的绝佳时机。