将可迭代的第一个 N 项转换为数组

Posted

技术标签:

【中文标题】将可迭代的第一个 N 项转换为数组【英文标题】:Convert first N item in iterable to Array 【发布时间】:2018-05-12 15:10:44 【问题描述】:

类似于问题Convert ES6 Iterable to Array。但我只想要前 N 项。有什么内置的让我这样做吗?或者我怎样才能更优雅地实现这一点?

let N = 100;
function *Z()  for (let i = 0; ; i++) yield i; 

// This wont work
// Array.from(Z()).slice(0, N);
// [...Z()].slice(0, N)

// This works, but a built-in may be preferred
let a = [], t = Z(); for (let i = 0; i < N; i++) a.push(t.next().value);

【问题讨论】:

让它成为一个函数。它们恰好适用于您不想重复(有时是不优雅)逻辑的这种情况。 【参考方案1】:

要获取iterator 的第一个n 值,您可以使用以下之一:

Array.from(length: n, function() return this.next().value; , iterator);
Array.from(length: n, (i => () => i.next().value)(iterator));

要获取任意iterableiterator,请使用:

const iterator = iterable[Symbol.iterator]();

在你的情况下,给定一个生成器函数Z

Array.from(length: 3, function() return this.next().value; , Z());

如果您更频繁地需要此功能,您可以创建一个生成器函数:

function* take(iterable, length) 
  const iterator = iterable[Symbol.iterator]();
  while (length-- > 0) yield iterator.next().value;


// Example:
const set = new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(...take(set, 3));

【讨论】:

我喜欢这种方法。你可以用[...take(set, 3)] 来美化它 @AluanHaddad 添加了一些甜味剂 如果从元素较少的迭代器中获取更多元素,则不起作用; @TomasJ - 没错,如果take() 生成器使用for...of + break 就像CRise's answer 一样会更好【参考方案2】:

没有内置方法可以只从可迭代对象中获取一定数量的项目(类似于take())。虽然您的 sn-p 可以通过 for of 循环得到一些改进,该循环专门用于使用可迭代对象,例如:

let a = []; let i = 0; for (let x of Z())  a.push(x); if (++i === N) break; 

这可能会更好,因为即使 iterable 中没有 N 个项目,您的原始 sn-p 也会继续循环。

【讨论】:

根据AirBnB linting 指南,for..of 循环会导致大量开销 @AyushGupta 有关于开销的证据吗?我认为这个答案是合理的,因为在给定的情况下没有这样的内置作品。 更新了一些 V8 引擎后性能似乎有所提高,但在几个版本之前似乎相当低 @AyushGupta 您的测试用例针对的是 Array,而不是 Iterable。那不一样。 我有 created a jsperf,似乎在 Chrome 中使用 for of 有点慢,但在 Firefox(52 和 57)中的性能相同。不知道为什么。【参考方案3】:

.map 更短,效率更低,自定义函数更安全:

function *Z()  for (let i = 0; i < 5; ) yield i++; 

function buffer(t, n = -1, a = [], c)  
    while (n-- && (c = t.next(), !c.done)) a.push(c.value); return a; 

const l = console.log, t = Z()

l( [...Array(3)].map(v => t.next().value) )

l( buffer(t) )

【讨论】:

【参考方案4】:

我怎样才能更优雅地实现这一点?

一种可能的优雅解决方案,使用 iter-ops 库:

import pipe, take from 'iter-ops';

const i = pipe(
    Z(), // your generator result
    take(N) // take up to N values
); //=> Iterable<number>

const arr = [...i]; // your resulting array

附:我是图书馆的作者。

【讨论】:

我想知道你们为什么要设计这样的界面?为什么不使用 Iter(Z()).take(N) Iter&lt;T&gt;::take(n: number) -&gt; Iter&lt;T&gt;Iter implements Iterable 之类的东西? @tsh 因为像您展示的那样链接需要使用合成类型(包装器),这会产生类型兼容性和集成问题,而我的方法仅适用于 javascript 本机类型 - 它需要一个可迭代的和输出一个可迭代的,并且它也以这种方式表现更好。

以上是关于将可迭代的第一个 N 项转换为数组的主要内容,如果未能解决你的问题,请参考以下文章

如何将可映射数组转换为 JSON?

Meteor - #每个数组的迭代,在每个第 n 项之后插入另一个 HTML 元素

从第 n 项迭代到数组末尾的 Perlish 方法是啥?

ES6 扩展运算符

06-01字符串格式化.md

06-01字符串格式化.md