Skip to content

Commit 9499650

Browse files
committed
Iterators for all JavaScript environments
0 parents  commit 9499650

File tree

9 files changed

+1355
-0
lines changed

9 files changed

+1355
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
*.swp
2+
*~
3+
.DS_Store
4+
/.nyc_output
5+
/coverage
6+
/node_modules
7+
npm-debug.log

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: node_js
2+
3+
node_js:
4+
- "6.1"
5+
6+
script: npm run travis

LICENSE

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Copyright (c) 2016 Lee Byron
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the "Software"), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7+
the Software, and to permit persons to whom the Software is furnished to do so,
8+
subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
# JavaScript [Iterators][] for all!
2+
3+
[![Build Status](https://travis-ci.org/leebyron/iterall.svg?branch=master)](https://travis-ci.org/leebyron/iterall) [![Coverage Status](https://coveralls.io/repos/github/leebyron/iterall/badge.svg?branch=master)](https://coveralls.io/github/leebyron/iterall?branch=master) ![568 bytes minified and gzipped](https://img.shields.io/badge/min%20gzip%20size-568%20B-blue.svg)
4+
5+
`iterall` provides a few crucial utilities for implementing and working with
6+
[Iterables][iterators] and [Array-likes][array-like] in all JavaScript
7+
environments, even old versions of Internet Explorer, in a tiny library weighing
8+
well under 1KB when minified and gzipped.
9+
10+
This is a library for libraries. If your library takes Arrays as input, accept
11+
Iterables instead. If your library implements a new data-structure, make
12+
it Iterable.
13+
14+
When installed via `npm`, `iterall` comes complete with [flow][] and
15+
[TypeScript][] definition files. Don't want to take the dependency? Feel free to
16+
copy code directly from this repository.
17+
18+
```js
19+
// Limited to only Arrays 😥
20+
if (Array.isArray(thing)) {
21+
thing.forEach(function (item, i) {
22+
console.log('Index: ' + i, item)
23+
})
24+
}
25+
26+
// Accepts all Iterables and Array-likes, in any JavaScript environment! 🎉
27+
var isCollection = require('iterall').isCollection
28+
var forEach = require('iterall').forEach
29+
30+
if (isCollection(thing)) {
31+
forEach(thing, function (item, i) {
32+
console.log('Index: ' + i, item)
33+
})
34+
}
35+
```
36+
37+
## Why use Iterators?
38+
39+
For most of JavaScript's history it has provided two collection data-structures:
40+
the `Object` and the `Array`. These collections can conceptually describe nearly
41+
all data and so it's no suprise that libraries expecting lists of
42+
things standardized on expecting and checking for an Array. This pattern even
43+
resulted in the addition of a new method in ES5: [`Array.isArray()`][isarray].
44+
45+
As JavaScript applications grew in complexity, moved to the [server][nodejs]
46+
where CPU is a constrained resource, faced new problems and implemented new
47+
algorithms, new data-structures are often required. With options from
48+
[linked lists][linked list] to [HAMTs][hamt] developers can use what is most
49+
efficient and provides the right properties for their program.
50+
51+
However none of these new data-structures can be used in libraries where an
52+
`Array` is expected, which means developers are often stuck between abandoning
53+
their favorite libraries or limiting their data-structure choices at the cost of
54+
efficiency or usefulness.
55+
56+
To enable many related data-structures to be used interchangably we need a
57+
_[protocol][]_, and luckily for us ES2015 introduced the
58+
[Iteration Protocols][iterators] to describe all list-like data-structures which
59+
can be iterated. That includes not just the new-to-ES2015 [Map][] and [Set][]
60+
collections but also existing ones like [arguments][], [NodeList][] and the
61+
various [TypedArray][], all of which return `false` for [`Array.isArray()`][isarray]
62+
and in ES2015 implement the [Iterator protocol][iterators].
63+
64+
While Iterators are defined in ES2015, they _do not require_ ES2015 to work
65+
correctly. In fact, Iterators were first introduced in 2012 in [Firefox v17](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#Iterator_property_and_iterator_symbol). Rather than using [`Symbol.iterator`][symbol.iterator], they used the property name `"@@iterator"` (in fact, the ECMAScript
66+
spec still refers to well-known `Symbols` using this `@@` shorthand). By falling
67+
back to use `"@@iterator"` when `Symbol.iterator` is not defined, Iterators can
68+
be both safely defined and used by _any version of JavaScript_.
69+
70+
Not only were Iterables defined in ES2015, they were also implemented by the
71+
built-in data-structures including [Array][array#@@iterator]. Older JavaScript
72+
environments do not implement `Array.prototype[@@iterator]()`, however this is
73+
only a minor problem. JavaScript has another related and much older protocol:
74+
[Array-like]. An value is "Array-like" if it has a numeric `length` property and
75+
indexed access, but does not necessarily have methods like `push` or `forEach`.
76+
Much like [`Array.from`][array.from], `iterall`'s `forEach()` and
77+
`createIterator()` methods also accept collections which are not Iterable but
78+
are Array-like. This means that `iterall` can be used with `Array`, `arguments`,
79+
`NodeList`, `TypedArray` and other Array-like collections regardless of the
80+
JavaScript environment.
81+
82+
When libraries only accept Arrays as input, they stick developers with a tough
83+
choice: limit which data-structures can be used or limit the ability to use that
84+
library. Accepting Iterables removes this false dichotomy, and allows libraries
85+
to be more generally useful. There's no need to limit to ES2015 environments and
86+
bleeding-edge browsers to leverage `Iterables`.
87+
88+
Only using Arrays can limit the efficiency and usefulness of your application
89+
code, but custom data-structures can often feel like a fish out of water in
90+
JavaScript programs, only working with code written specifically for it.
91+
Protocols like `Iterable` helps these new data-structures work with more
92+
libraries and built-in JavaScript behavior. There's no need to limit to ES2015
93+
environments and bleeding-edge browsers to implement `Iterable`.
94+
95+
## API
96+
97+
### $$ITERATOR
98+
99+
A property name to be used as the name of an Iterable's method reponsible
100+
for producing an Iterator. Typically represents the value `Symbol.iterator`.
101+
102+
`Symbol` is defined in ES2015 environments, however some transitioning
103+
JavaScript environments, such as older versions of Node define Symbol, but
104+
do not define `Symbol.iterator`. Older versions of Mozilla Firefox,
105+
which originally introduced the Iterable protocol, used the string
106+
value `"@@iterator"`. This string value is used when Symbol.iterator is
107+
not defined.
108+
109+
Use `$$ITERATOR` for defining new Iterables instead of `Symbol.iterator`,
110+
but do not use it for accessing existing Iterables, instead use `getIterator`
111+
or `isIterable`.
112+
113+
**Examples**
114+
115+
```javascript
116+
var $$ITERATOR = require('iterall').$$ITERATOR
117+
118+
function Counter(to) {
119+
this.to = to
120+
this.num = 0
121+
}
122+
123+
Counter.prototype[$$ITERATOR] = function () {
124+
if (this.num >= this.to) {
125+
return { value: undefined, done: true }
126+
}
127+
return { value: this.num++ }
128+
}
129+
```
130+
131+
### isIterable
132+
133+
Returns true if the provided object implements the Iterator protocol via
134+
either implementing Symbol.iterator or '@@iterator' method.
135+
136+
**Parameters**
137+
138+
- `obj` **Any** A value which might be implement the Iterable protocol.
139+
140+
**Examples**
141+
142+
```javascript
143+
var isIterable = require('iterall').isIterable
144+
isIterable([ 1, 2, 3 ]) // true
145+
isIterable('ABC') // true
146+
isIterable({ key: 'value' }) // false
147+
```
148+
149+
Returns **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** true if Iterable.
150+
151+
### isCollection
152+
153+
Returns true if the provided object is an Object (i.e. not a string literal)
154+
and is either "Array-like" due to having a numeric `length` property, or is
155+
Iterable.
156+
157+
This may be used in place of `Array.isArray(obj)` to determine if an object
158+
can be iterated-over. It always excludes string literals and includes Arrays
159+
(regardless of if it is Iterable). It also includes other Array-like objects
160+
such as NodeList, TypedArray, and Buffer. It also includes any Object which
161+
implements the Iterable protocol.
162+
163+
**Parameters**
164+
165+
- `obj` **Any** An Object value which might implement the Iterable or Array-like protocols.
166+
167+
**Examples**
168+
169+
```javascript
170+
var isCollection = require('iterall').isCollection
171+
var forEach = require('iterall').forEach
172+
if (isCollection(obj)) {
173+
forEach(obj, function (value) {
174+
console.log(value)
175+
})
176+
}
177+
```
178+
179+
Returns **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** true if Iterable or Array-like Object.
180+
181+
### getIterator
182+
183+
If the provided object implements the Iterator protocol, its Iterator object
184+
is returned. Otherwise returns undefined.
185+
186+
**Parameters**
187+
188+
- `iterable` **Iterable<T>** An Iterable object which is the source of an Iterator.
189+
190+
**Examples**
191+
192+
```javascript
193+
var getIterator = require('iterall').getIterator
194+
var iterator = getIterator([ 1, 2, 3 ])
195+
iterator.next() // { value: 1, done: false }
196+
iterator.next() // { value: 2, done: false }
197+
iterator.next() // { value: 3, done: false }
198+
iterator.next() // { value: undefined, done: true }
199+
```
200+
201+
Returns **Iterator<T>** new Iterator instance.
202+
203+
### getIteratorMethod
204+
205+
If the provided object implements the Iterator protocol, the method
206+
responsible for producing its Iterator object is returned.
207+
208+
This is used in rare cases for performance tuning. This method must be called
209+
with obj as the contextual this-argument.
210+
211+
**Parameters**
212+
213+
- `iterable` **Iterable<T>** An Iterable object which defines an `@@iterator` method.
214+
215+
**Examples**
216+
217+
```javascript
218+
var getIteratorMethod = require('iterall').getIteratorMethod
219+
var myArray = [ 1, 2, 3 ]
220+
var method = getIteratorMethod(myArray)
221+
if (method) {
222+
var iterator = method.call(myArray)
223+
}
224+
```
225+
226+
Returns **function (): Iterator<T>** @@iterator method.
227+
228+
### forEach
229+
230+
Given an object which is either Array-like (by having a numeric length
231+
property) or implements the Iterable protocol, iterate over it, calling the
232+
`callback` at each iteration.
233+
234+
Similar to Array#forEach, the `callback` function accepts three arguments,
235+
and is provided with `thisArg` as the calling context.
236+
237+
`forEach` adheres to the behavior described in the ECMAScript specification,
238+
skipping over "holes" in Arrays and Array-likes.
239+
240+
Note: providing an infinite Iterator to forEach will produce an error.
241+
242+
**Parameters**
243+
244+
- `collection` **(Iterable<T> | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<T>)** The Iterable or array to iterate over.
245+
- `callback` **function (T, [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), \[[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)])** Function to execute for each iteration, taking up to three arguments
246+
- `thisArg` Optional. Value to use as `this` when executing `callback`.
247+
248+
**Examples**
249+
250+
```javascript
251+
var forEach = require('iterall').forEach
252+
253+
forEach(myIterable, function (value, index) {
254+
console.log(value, index)
255+
})
256+
```
257+
258+
### createIterator
259+
260+
Similar to `getIterator(obj)`, this method returns a new Iterator given an
261+
Iterable. However it will also create an Iterator for a non-Iterable
262+
Array-like collection, such as Array in a non-ES2015 environment.
263+
264+
`createIterator` is complimentary to `forEach`, but allows a "pull"-based
265+
iteration as opposed to `forEach`'s "push"-based iteration.
266+
267+
`createIterator` produces an Iterator for Array-likes with the same behavior
268+
as ArrayIteratorPrototype described in the ECMAScript specification, and
269+
does _not_ skip over "holes".
270+
271+
**Parameters**
272+
273+
- `collection` **(Iterable<T> | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<T>)** An Iterable or Array-like object to produce an Iterator.
274+
275+
**Examples**
276+
277+
```javascript
278+
var createIterator = require('iterall').createIterator
279+
280+
var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' }
281+
var iterator = createIterator(myArraylike)
282+
iterator.next() // { value: 'Alpha', done: false }
283+
iterator.next() // { value: 'Bravo', done: false }
284+
iterator.next() // { value: 'Charlie', done: false }
285+
iterator.next() // { value: undefined, done: true }
286+
```
287+
288+
Returns **Iterator<T>** new Iterator instance.
289+
290+
## Contributing
291+
292+
Contributions are welcome and encouraged!
293+
294+
Remember that this library is designed to be small, straight-forward, and
295+
well-tested. The value of new additional features will be weighed against their
296+
size. This library also seeks to leverage and mirror the
297+
[ECMAScript specification][] in its behavior as much as possible and reasonable.
298+
299+
This repository has far more documentation and explaination than code, and it is
300+
expected that the majority of contributions will come in the form of improving
301+
these.
302+
303+
<!-- Appendix -->
304+
305+
[arguments]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
306+
307+
[array#@@iterator]: (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@iterator)
308+
309+
[array-like]: http://www.2ality.com/2013/05/quirk-array-like-objects.html
310+
311+
[array.from]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
312+
313+
[ecmascript specification]: http://www.ecma-international.org/ecma-262/6.0/
314+
315+
[flow]: https://flowtype.org/
316+
317+
[hamt]: https://en.wikipedia.org/wiki/Hash_array_mapped_trie
318+
319+
[isarray]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
320+
321+
[iterators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
322+
323+
[linked list]: https://en.wikipedia.org/wiki/Linked_list
324+
325+
[map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
326+
327+
[nodejs]: https://nodejs.org/
328+
329+
[nodelist]: https://developer.mozilla.org/en-US/docs/Web/API/NodeList
330+
331+
[protocol]: https://en.wikipedia.org/wiki/Protocol_(object-oriented_programming)
332+
333+
[set]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
334+
335+
[symbol.iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator
336+
337+
[typedarray]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
338+
339+
[typescript]: http://www.typescriptlang.org/

0 commit comments

Comments
 (0)