Skip to content

Commit 342a79f

Browse files
committed
Initial Commit
0 parents  commit 342a79f

File tree

6 files changed

+860
-0
lines changed

6 files changed

+860
-0
lines changed

IArray.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/* global define */
2+
3+
(function(global, factory) {
4+
if(typeof define === "function" && define.amd)
5+
define(factory)
6+
else if(typeof eki === "object" && eki.def)
7+
eki.def("IArray", factory)
8+
else if(typeof exports === "object")
9+
module.exports = factory()
10+
}(this, function() {
11+
12+
"use strict"
13+
14+
// These methods work fine as they are in Array and are non-destructive,
15+
// but they return an array, so just call the native method and upgrade
16+
// the returned array to an IArray
17+
var passAndReturn = ["filter", "map", "slice"]
18+
.reduce(function(p, c) {
19+
p[c] = function() {
20+
return IArray(Array.prototype[c].apply(this, arguments))
21+
}
22+
return p
23+
}, {})
24+
25+
function concat()
26+
{
27+
var a2 = this.toArray() // first, clone ourselve to a standard Array
28+
29+
for(var argi = 0;argi < arguments.length;argi++)
30+
{
31+
var arg = arguments[argi]
32+
if(arg && arg.isIArray)
33+
a2 = a2.concat(arg.toArray())
34+
else
35+
a2 = a2.concat(arg)
36+
}
37+
38+
return IArray(a2)
39+
}
40+
41+
// These methods are mutating, so first copy the array, then
42+
// operate on the copy, then return a new IA2
43+
var copyAndOperate = [ "copyWithin", "fill", "push", "pop", "reverse", "shift",
44+
"unshift", "sort", "splice"].reduce(function(p, c) {
45+
p[c] = function() {
46+
// create a mutable IArray object "clone" of this
47+
var a2 = createIArray(this),
48+
// perform the operation on it
49+
ret = Array.prototype[c].apply(a2, arguments)
50+
51+
// if the returned value is a native array, convert to IArray
52+
if(Array.isArray(ret))
53+
ret = IArray(ret)
54+
55+
// store the return value
56+
a2.ret = ret // place the return value here
57+
58+
// Finally, freeze it and return
59+
return deepFreeze(a2)
60+
}
61+
return p
62+
}, {})
63+
64+
function set(index, value)
65+
{
66+
var a2 = createIArray(this)
67+
a2[index] = value
68+
return deepFreeze(a2)
69+
}
70+
71+
// Create our custom IArray prototype, with Array.prototype at the base
72+
var IAProto = Object.assign(
73+
Object.create(Array.prototype),
74+
passAndReturn,
75+
copyAndOperate,
76+
{
77+
concat: concat,
78+
isIArray: true,
79+
set: set,
80+
toArray: function() { return Array.prototype.slice.call(this) }
81+
}
82+
)
83+
84+
function deepFreeze(o)
85+
{
86+
if(IArray.freeze === "SHALLOW" || IArray.freeze === "DEEP")
87+
{
88+
Object.freeze(o)
89+
90+
if(IArray.freeze === "DEEP")
91+
Object.getOwnPropertyNames(o)
92+
.forEach(function(n) {
93+
if((typeof o[n] === "object" || typeof o[n] === "function") && !Object.isFrozen(o[n]))
94+
deepFreeze(o[n])
95+
})
96+
}
97+
98+
return o
99+
}
100+
101+
// Create hte IArray object. fromArray may be a native Array
102+
// or an IArray
103+
function createIArray(fromArray)
104+
{
105+
return Object.assign(
106+
Object.create(IAProto),
107+
fromArray,
108+
fromArray ? { length: fromArray.length } : null
109+
)
110+
}
111+
112+
function IArray(fromArray)
113+
{
114+
return deepFreeze(createIArray(fromArray))
115+
}
116+
117+
return IArray
118+
119+
}))

LICENSE

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

README.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# IArray
2+
3+
An Immutable Array that looks, behaves (and IS) a standard JavaScript Array but with immutable properties.
4+
5+
---
6+
7+
## Why
8+
9+
One of the cornerstones of functional programming is immutable data structures. Using immutable data structures insures to the author and other programmers that once they have a handle to an object, it won't change beneath their feet. This is important for reasoning about code. It aids with testing, debugging and refactoring. It provides a much faster way to determine if state has changed. And it helps you write pure functions.
10+
11+
Arrays are an extremely popular data structure in most JavaScript programs. But they are far from immutable. One can simply assign values to an array at a given index via `myArray[10] = 100`. And many of the Array methods are mutating, such as `push`, `sort` and `slice`.
12+
13+
Some libraries exist that provide random access data structures similar to the Array but that are immutable, such as [Mori](http://swannodette.github.io/mori/) or [Immutable](https://facebook.github.io/immutable-js/) - but they are large opinionated libraries that expose a completely different API.
14+
15+
**IArray** provides an Immutable array only, and is very light.
16+
17+
```bash
18+
wc -l IArray.js
19+
119 IArray.js
20+
```
21+
22+
119 lines in the *source* file, much of which is comments and the universal module definition.
23+
24+
## How
25+
26+
`IArray` extends the `Array` object by building a prototype which includes `Array.prototype`. This enables `IArray` to offer all the API methods contained in the standard `Array`, and appear to debuggers and most other type checkers as an `Array`
27+
28+
```javascript
29+
const a = IArray()
30+
if(a instanceof Array) // true!
31+
// ...
32+
```
33+
34+
But `IArray` intercepts calls to mutating methods, such as `push` and internally clones the array, calls the `Array.push` using your same arguments, and finally returning a new `IArray` with the result.
35+
36+
So mutating methods all exist, but their behavior is slightly different - returning a new `IArray` with the changes applied, rather than mutating the array upon which its called.
37+
38+
In cases where a method mutated the underlying array and also returned a value (such as `pop`), the value is available at `IArray.ret`.
39+
40+
## API
41+
42+
Since this extends the standard JavaScript `Array`, I will only document the methods that have *changed* from the standard Array API:
43+
44+
45+
### `concat(value1[, value2 ...]) => IArray`
46+
47+
Behaves just as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) `Array.concat` with the exception that the returned array is an `IArray`.
48+
49+
```javascript
50+
IArray([1, 2, 3]).concat([7, 8]) // => IArray([1, 2, 3, 7, 8])
51+
IArray().concat(IArray([3, 4, 5]) // => IArray([3, 4, 5])
52+
IArray([1, 2, 3]).concat(100) // => IArray([1, 2, 3, 100])
53+
```
54+
55+
56+
### `copyWithin(target[, start[, end]]) => IArray`
57+
58+
Behaves just as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin) `Array.copyWithin` with the exception that the returned array is an `IArray` and the original array is not modified
59+
60+
```javascript
61+
var a = IArray([5, 6, 7, 8])
62+
var b = a.copyWithin(2) // => IArray([5, 6, 5, 6]) (a unmodified)
63+
```
64+
65+
### `fill(target[, start[, end]]) => IArray`
66+
67+
Behaves just as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill) `Array.fill` with the exception that the returned array is an `IArray`.
68+
69+
```javascript
70+
var a1 = IArray([1, 2, 3, 4, 5])
71+
var a2 = a1.fill(9, 2, 4)
72+
// a1 = IArray([1, 2, 3, 4, 5]) - this shouldn't change
73+
// a2 = IArray([1, 2, 9, 9, 5]) - fill 9s starting at item 2, ending at 4
74+
```
75+
76+
### `filter(fn[, thisArg]) => IArray`
77+
78+
Behaves just as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) `Array.filter` with the exception that the returned array is an `IArray`.
79+
80+
```javascript
81+
function even(n) { return n % 2 === 0 }
82+
83+
var a1 = IArray([1, 2, 3, 4, 5, 6])
84+
var a2 = a1.filter(even)
85+
// a1 = IArray([1, 2, 3, 4, 5, 6]) - this shouldn't change
86+
// a2 = IArray([2, 4, 6]) - even values only
87+
```
88+
89+
### `map(fn[, thisArg]) => IArray`
90+
91+
Behaves just as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) `Array.map` with the exception that the returned array is an `IArray`.
92+
93+
```javascript
94+
var a1 = IArray([1, 4, 9, 25])
95+
var a2 = a1.map(Math.sqrt)
96+
// a1 = IArray([1, 4, 9, 25]) - ensure it is unchanged
97+
// a2 = IArray([1, 2, 3, 5]) - mapped values
98+
```
99+
100+
### `push(value1, ..., valuen) => IArray`
101+
102+
The same API signature as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) `Array.push`, but the original `IArray` is not modified. A new `IArray` is returned with the value(s) appended, and the length property is stored in the `ret` property (which is mostly redundant since it is also available in the `length` property - but is included for consistency)
103+
104+
```javascript
105+
var a1 = IArray([1, 4, 9])
106+
var a2 = a1.push(100)
107+
// a1 = IArray([1, 4, 9])
108+
// a2 = IArray([1, 4, 9, 100])
109+
```
110+
111+
### `pop() => IArray`
112+
113+
The same API signature as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) `Array.pop`, but the original `IArray` is not modified. A new `IArray` is returned with the last value removed, and the value that was removed is stored in the `ret` property.
114+
115+
```javascript
116+
var a1 = IArray([1, 4, 9])
117+
var a2 = a1.pop()
118+
// a1 = IArray([1, 4, 9]) - unchanged
119+
// a2 = IArray([1, 4]) - same as a1 with last element removed
120+
// a2.ret = 9 - last element stored in ret property
121+
```
122+
123+
### `shift() => IArray`
124+
125+
The same API signature as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) `Array.shift`, but the original `IArray` is not modified. A new `IArray` is returned with the first element removed. This removed element is available through the `ret` property of the returned `IArray`.
126+
127+
```javascript
128+
var a1 = IArray([1, 4, 9])
129+
var a2 = a1.shift()
130+
131+
// a1 = IArray([1, 4, 9]) - no change
132+
// a2 = IArray([4, 9]) - first value removed
133+
// a2.ret = 1 - removed value
134+
```
135+
136+
### `slice([begin[, end]]) => IArray`
137+
138+
Behaves just as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) `Array.slice` with the exception that the returned array is an `IArray`.
139+
140+
```javascript
141+
var a1 = IArray([5, 10, 15, 20])
142+
var a2 = a1.slice(1, 3)
143+
// a1 = IArray([5, 10, 15, 20])) - no changes
144+
// a2 = IArray([10, 15])) - elements 1 to 3 (non-inclusive)
145+
```
146+
147+
### `sort([compareFn]) => IArray`
148+
149+
Behaves just like the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) `Array.sort` with the exception that the original `IArray` is not modified, and the newly sorted `IArray` is returned.
150+
151+
```javascript
152+
var fruit = ["cherries", "apples", "bananas", "pears"]
153+
var a1 = IArray(fruit)
154+
var a2 = a1.sort()
155+
var a3 = a1.sort(function(a, b) { return a.length - b.length }) // sort by word length
156+
// a1 remains unchanged: IArray(["cherries", "apples", "bananas", "pears"])
157+
// a2 is sorted: IArray(["apples", "bananas", "cherries", "pears"])
158+
// a3 is sorted according to comparitor: IArray(["pears", "apples", "bananas", "cherries"])
159+
```
160+
161+
### `splice([start[, deleteCount[, value1, value2, ...]]]) => IArray`
162+
163+
Behaves just like the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) `Array.splice` with the exception that the original `IArray` is not modified but is returned as a new `IArray`. The removed items will be stored as an `IArray` on the `ret` property.
164+
165+
```javascript
166+
var a1 = IArray([5, 10, 15, 20])
167+
var a2 = a1.splice(1, 1, 22)
168+
// a1 is unchanged
169+
// a2 is IArray([5, 22, 15, 20]) - item 1 removed and replaced with 22
170+
// a2.ret is IArray([10]) - the removed items in an IArray
171+
```
172+
173+
174+
### `unshift([element1, ..., elementN]) => IArray`
175+
176+
The same API signature as the [standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift) `Array.unshift`, but the original `IArray` is not modified. A new `IArray` is returned with elements added to the front of the array.
177+
178+
```javascript
179+
var a1 = IArray([5, 10, 20])
180+
var a2 = a1.unshift("hello", "world")
181+
182+
// a1 = IArray([5, 10, 20])) - unchanged
183+
// a2 = IArray(["hello", "world", 5, 10, 20]))
184+
```
185+
186+
187+
188+
### License
189+
190+
See the LICENSE file for license rights and limitations (MIT).

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "IArray",
3+
"version": "1.0.0",
4+
"description": "Immutable Array",
5+
"main": "IArray.js",
6+
"scripts": {
7+
"test": "tarsy test"
8+
},
9+
"license": "MIT",
10+
"author": "Glenn Crownover <glenn@bluejava.com> (http://www.bluejava.com)",
11+
"dependencies": {},
12+
"devDependencies": {
13+
"tarsy": "^0.5.1"
14+
}
15+
}

0 commit comments

Comments
 (0)