diff --git a/__tests__/Stretch.ts b/__tests__/Stretch.ts new file mode 100644 index 000000000..615a409f4 --- /dev/null +++ b/__tests__/Stretch.ts @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/// + +import * as jasmineCheck from 'jasmine-check'; +jasmineCheck.install(); + +import { Stretch } from '../'; + +describe('Stretch', () => { + it('fixed stretch', () => { + const v = Stretch(5, 3); + expect(v.size).toBe(3); + expect(v.first()).toBe(2); + expect(v.rest().toArray()).toEqual([5, 8]); + expect(v.last()).toBe(8); + expect(v.butLast().toArray()).toEqual([2, 5]); + expect(v.toArray()).toEqual([2, 5, 8]); + }); + + it('ends and front', () => { + const v = Stretch(5, 3, 4); + expect(v.size).toBe(3); + expect(v.first()).toBe(2); + expect(v.rest().toArray()).toEqual([5, 9]); + expect(v.last()).toBe(9); + expect(v.butLast().toArray()).toEqual([2, 5]); + expect(v.toArray()).toEqual([2, 5, 9]); + }); + + it('slices stretch', () => { + const v = Stretch(5, 3, 4); + const s = v.slice(0, 2); + expect(s.size).toBe(2); + expect(s.toArray()).toEqual([2, 5]); + }); + + it('one value', () => { + const v = Stretch(5, 3, 4); + const s = v.slice(0, 1); + expect(s.size).toBe(1); + expect(s.toArray()).toEqual([2]); + }); + + it('maps values', () => { + const r = Stretch(5, 3).map(v => v * v); + expect(r.toArray()).toEqual([4, 25, 64]); + }); + + it('reduces values', () => { + const v = Stretch(5, 3); + const r = v.reduce((a, b) => a + b, 0); + expect(r).toEqual(15); + }); + + it('can be float', () => { + const v = Stretch(3.5, 1.5, 2.5); + expect(v.size).toBe(3); + expect(v.toArray()).toEqual([2, 3.5, 6]); + }); + + it('can be negative', () => { + const v = Stretch(-5, -5, -6); + expect(v.size).toBe(3); + expect(v.toArray()).toEqual([0, -5, -11]); + }); +}); diff --git a/src/Immutable.js b/src/Immutable.js index de672c8a7..336c93f1e 100644 --- a/src/Immutable.js +++ b/src/Immutable.js @@ -15,6 +15,7 @@ import { Set } from './Set'; import { Record } from './Record'; import { Range } from './Range'; import { Repeat } from './Repeat'; +import { Stretch } from './Stretch'; import { is } from './is'; import { fromJS } from './fromJS'; @@ -71,6 +72,7 @@ export default { Record: Record, Range: Range, Repeat: Repeat, + Stretch: Stretch, is: is, fromJS: fromJS, @@ -125,6 +127,7 @@ export { Record, Range, Repeat, + Stretch, is, fromJS, hash, diff --git a/src/Stretch.js b/src/Stretch.js new file mode 100644 index 000000000..b56ecb25a --- /dev/null +++ b/src/Stretch.js @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { wholeSlice, resolveBegin, resolveEnd } from './TrieUtils'; +import { IndexedSeq } from './Seq'; +import invariant from './utils/invariant'; +import deepEqual from './utils/deepEqual'; +import { Iterator, iteratorValue, iteratorDone } from './Iterator'; + +/** + * Returns a lazy seq of base - ends, base, base + front, + * if front is not provided it returns base + ends, + * `base` and `ends` both default to 1 + */ +export class Stretch { + constructor(base, ends, front) { + if (!(this instanceof Stretch)) { + return new Stretch(base, ends, front); + } + invariant( + base !== Infinity || ends !== Infinity || front !== Infinity, + `Must be a finite number` + ); + invariant(base !== undefined, `Base is required`); + ends = ends === undefined ? 1 : ends; + front = front === undefined ? ends : front; + this._base = base; + this._start = base - ends; + this._end = base + front; + this.size = 3; + /*when i tried extending IndexedSeq or ArraySeq and calling super + the tests __tests__/Stretch.ts returned [5, 8, undefined] instead of 2, 5, 8] + and [5, 9, undefined] instead of [2, 5, 9]*/ + return new IndexedSeq([this._start, this._base, this._end]); + } + toString() { + return `[${this._start}, ${this._base}, ${this._end}]`; + } + + get(index, notSetValue) { + return index === 0 + ? this._start + : index === 1 + ? this._base + : index === 2 + ? this._end + : notSetValue; + } + + includes(searchValue) { + return ( + searchValue === this._start || + searchValue === this._base || + searchValue === this._end + ); + } + + slice(begin, end) { + if (wholeSlice(begin, end, this.size)) { + return this; + } + begin = resolveBegin(begin, this.size); + end = resolveEnd(end, this.size); + return end <= begin + ? new IndexedSeq([]) + : end - begin === 1 + ? new IndexedSeq([this.get(begin)]) + : new IndexedSeq([this.get(begin), this.get(begin + 1)]); + } + + indexOf(searchValue) { + return searchValue === this._start + ? 0 + : searchValue === this._base + ? 1 + : searchValue === this._end + ? 2 + : -1; + } + /*we can't return indexOf for lastIndexOf because ends and front can +be be different values*/ + lastIndexOf(searchValue) { + return searchValue === this._end + ? 2 + : searchValue === this._base + ? 1 + : searchValue === this._start + ? 0 + : -1; + } + + __iterate(fn, reverse) { + const size = 3; + let value = reverse ? this._end : this._start; + let i = 0; + value += reverse ? -(this._end - this._base) : this._base - this._start; + if (fn(value, reverse ? size - ++i : i++, this) === false) { + return i; + } + value += reverse ? -(this._base - this._start) : this._end - this._base; + if (fn(value, reverse ? size - ++i : i++, this) === false) { + return i; + } + return i; + } + + __iterator(type, reverse) { + const array = [this._start, this._base, this._end]; + const size = 3; + let i = 0; + return new Iterator(() => { + if (i === size) { + return iteratorDone(); + } + const ii = reverse ? size - ++i : i++; + return iteratorValue(type, ii, array[ii]); + }); + } + + reverse() { + return new Stretch(this._base, -this._ends, -this._front); + } + + interpose(separator) { + return new IndexedSeq([ + this._start, + separator, + this._base, + separator, + this._end, + ]); + } + + rest() { + return new IndexedSeq([this._base, this._end]); + } + + butLast() { + return new IndexedSeq([this._start, this._base]); + } + + equals(other) { + return other instanceof Stretch + ? this._start === other._start && + this._end === other._end && + this._base === other._base + : deepEqual(this, other); + } +} diff --git a/type-definitions/Immutable.d.ts b/type-definitions/Immutable.d.ts index baae41d01..928bf91da 100644 --- a/type-definitions/Immutable.d.ts +++ b/type-definitions/Immutable.d.ts @@ -2203,6 +2203,23 @@ declare module Immutable { */ export function Repeat(value: T, times?: number): Seq.Indexed; + /** + * Returns a Seq.Indexed with `base` - `ends`, `base`, and `base` + `front`, if + * front is not provided it returns `base` + `ends`, `base` is required and `ends` + * defaults to 1 + * + * Note: `Stretch` is a factory function and not a class, and does not use the + * `new` keyword during construction. + * + * ```js + * const { Stretch } = require('immutable') + * Stretch(10, 5) // [5, 10, 15] + * Stretch(10, 5, 3) // [5, 10, 13] + * Stretch(5) // [4, 5, 6] + * ``` + */ + export function Stretch(base: number, ends?: number, front?: number): Seq.Indexed; + /** * A record is similar to a JS object, but enforces a specific set of allowed diff --git a/type-definitions/immutable.js.flow b/type-definitions/immutable.js.flow index 2b37f4143..64e76dca9 100644 --- a/type-definitions/immutable.js.flow +++ b/type-definitions/immutable.js.flow @@ -1380,6 +1380,7 @@ declare class Stack<+T> extends IndexedCollection { declare function Range(start?: number, end?: number, step?: number): IndexedSeq; declare function Repeat(value: T, times?: number): IndexedSeq; +declare function Stretch(base: number, ends?: number, front?: number): IndexedSeq; // The type of a Record factory function. type RecordFactory = Class>; @@ -1581,6 +1582,7 @@ export { OrderedSet, Range, Repeat, + Stretch Record, Set, Stack, @@ -1624,6 +1626,7 @@ export default { OrderedSet, Range, Repeat, + Stretch, Record, Set, Stack, diff --git a/type-definitions/tests/immutable-flow.js b/type-definitions/tests/immutable-flow.js index 0bc7289b0..33fc14b31 100644 --- a/type-definitions/tests/immutable-flow.js +++ b/type-definitions/tests/immutable-flow.js @@ -18,6 +18,7 @@ import Immutable, { Seq, Range, Repeat, + Stretch, Record, OrderedMap, OrderedSet, @@ -64,6 +65,7 @@ const ImmutableSet = Immutable.Set const ImmutableKeyedCollection: KeyedCollection<*, *> = Immutable.Collection.Keyed() const ImmutableRange = Immutable.Range const ImmutableRepeat = Immutable.Repeat +const ImmutableStretch = Immutable.Stretch const ImmutableIndexedSeq: IndexedSeq<*> = Immutable.Seq.Indexed() const Immutable2List = Immutable2.List @@ -73,6 +75,7 @@ const Immutable2Set = Immutable2.Set const Immutable2KeyedCollection: Immutable2.KeyedCollection<*, *> = Immutable2.Collection.Keyed() const Immutable2Range = Immutable2.Range const Immutable2Repeat = Immutable2.Repeat +const Immutable2Stretch = Immutable2.Stretch const Immutable2IndexedSeq: Immutable2.IndexedSeq<*> = Immutable2.Seq.Indexed() var defaultExport: List<*> = Immutable.List(); @@ -884,17 +887,20 @@ numberStack = Stack([1]).flatMap((value, index, iter) => ['a']) numberStack = Stack([1]).flatten() numberStack = Stack(['a']).flatten() -/* Range & Repeat */ +/* Range & Repeat & Stretch */ // `{}` provide namespaces { const numberSequence: IndexedSeq = Range(0, 0, 0) } { const numberSequence: IndexedSeq = Repeat(1, 5) } +{ const numberSequence: IndexedSeq = Stretch(5, 3) } { const stringSequence: IndexedSeq = Repeat('a', 5) } // $ExpectError { const stringSequence: IndexedSeq = Repeat(0, 1) } // $ExpectError { const stringSequence: IndexedSeq = Range(0, 0, 0) } +// $ExpectError +{ const stringSequence: IndexedSeq = Stretch('a', 3) } /* Seq */ diff --git a/type-definitions/ts-tests/exports.ts b/type-definitions/ts-tests/exports.ts index 8da25cda0..4190570a1 100644 --- a/type-definitions/ts-tests/exports.ts +++ b/type-definitions/ts-tests/exports.ts @@ -15,6 +15,7 @@ import { OrderedSet, Range, Repeat, + Stretch, Seq, Set, Stack, @@ -28,6 +29,7 @@ OrderedSet; // $ExpectType typeof OrderedSet // TODO: Turn on once https://github.com/Microsoft/dtslint/issues/19 is resolved. Range; // $ ExpectType (start?: number | undefined, end?: number | undefined, step?: number | undefined) => Indexed Repeat; // $ExpectType (value: T, times?: number | undefined) => Indexed +Stretch; // $ExpectType (base: number, ends?: number | undefined, front?: number | undefined) => Indexed Seq; // $ExpectType typeof Seq Set; // $ExpectType typeof Set Stack; // $ExpectType typeof Stack @@ -43,6 +45,7 @@ Immutable.OrderedSet; // $ExpectType typeof OrderedSet // TODO: Turn on once https://github.com/Microsoft/dtslint/issues/19 is resolved. Immutable.Range; // $ ExpectType (start?: number | undefined, end?: number | undefined, step?: number | undefined) => Indexed Immutable.Repeat; // $ExpectType (value: T, times?: number | undefined) => Indexed +Immutable.Stretch; // $ExpectType (base: number, ends?: number | undefined, front?: number | undefined) => Indexed Immutable.Seq; // $ExpectType typeof Seq Immutable.Set; // $ExpectType typeof Set Immutable.Stack; // $ExpectType typeof Stack diff --git a/type-definitions/ts-tests/stretch.ts b/type-definitions/ts-tests/stretch.ts new file mode 100644 index 000000000..0c495b07e --- /dev/null +++ b/type-definitions/ts-tests/stretch.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Stretch } from '../../'; + +{ // #constructor + + // $ExpectType Indexed + Stretch(5, 3); + + // $ExpectError + Stretch('a', 3); +}