Skip to content

Commit

Permalink
feat(arrays): Add index to callbacks
Browse files Browse the repository at this point in the history
Add index argument to iterable callbacks.

re #11
  • Loading branch information
danielrbradley committed Jul 24, 2018
1 parent 7e7cce4 commit c2ec011
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 33 deletions.
89 changes: 56 additions & 33 deletions src/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ export function ofIterable<T>(source: Iterable<T>): T[] {
* Creates a new array whose elements are the results of applying the specified mapping to each of the elements of the source collection.
* @param mapping A function to transform items from the input collection.
*/
export function map<T, U>(mapping: (item: T) => U): (source: T[]) => U[]
export function map<T, U>(mapping: (item: T, index: number) => U): (source: T[]) => U[]
/**
* Creates a new array whose elements are the results of applying the specified mapping to each of the elements of the source collection.
* @param source The input collection.
* @param mapping A function to transform items from the input collection.
*/
export function map<T, U>(source: T[], mapping: (item: T) => U): U[]
export function map<T, U>(source: T[], mapping: (item: T, index: number) => U): U[]
export function map<T, U>(a: any, b?: any): any {
const partial = typeof a === 'function'
const mapping: (item: T) => U = partial ? a : b
const mapping: (item: T, index: number) => U = partial ? a : b
function exec(source: T[]) {
return source.map(mapping)
}
Expand All @@ -30,16 +30,16 @@ export function map<T, U>(a: any, b?: any): any {
* Returns a new array containing only the elements of the collection for which the given predicate returns true.
* @param predicate A function to test whether each item in the input collection should be included in the output.
*/
export function filter<T>(predicate: (item: T) => boolean): (source: T[]) => T[]
export function filter<T>(predicate: (item: T, index: number) => boolean): (source: T[]) => T[]
/**
* Returns a new array containing only the elements of the collection for which the given predicate returns true.
* @param source The input collection.
* @param predicate A function to test whether each item in the input collection should be included in the output.
*/
export function filter<T>(source: T[], predicate: (item: T) => boolean): T[]
export function filter<T>(source: T[], predicate: (item: T, index: number) => boolean): T[]
export function filter<T, U>(a: any, b?: any): any {
const partial = typeof a === 'function'
const predicate: (item: T) => boolean = partial ? a : b
const predicate: (item: T, index: number) => boolean = partial ? a : b
function exec(source: T[]) {
return source.filter(predicate)
}
Expand All @@ -50,23 +50,27 @@ export function filter<T, U>(a: any, b?: any): any {
* Applies the given function to each element of the array and returns a new array comprised of the results for each element where the function returns a value.
* @param chooser A function to transform items from the input collection to a new value to be included, or undefined to be excluded.
*/
export function choose<T, U>(chooser: (item: T) => U | undefined): (source: T[]) => U[]
export function choose<T, U>(
chooser: (item: T, index: number) => U | undefined
): (source: T[]) => U[]
/**
* Applies the given function to each element of the array and returns a new array comprised of the results for each element where the function returns a value.
* @param source The input collection.
* @param chooser A function to transform items from the input collection to a new value to be included, or undefined to be excluded.
*/
export function choose<T, U>(source: T[], chooser: (item: T) => U | undefined): U[]
export function choose<T, U>(source: T[], chooser: (item: T, index: number) => U | undefined): U[]
export function choose<T, U>(a: any, b?: any): any {
const partial = typeof a === 'function'
const chooser: (item: T) => U | undefined = partial ? a : b
const chooser: (item: T, index: number) => U | undefined = partial ? a : b
function exec(source: T[]) {
const target = []
let index = 0
for (const item of source) {
const chosen = chooser(item)
const chosen = chooser(item, index)
if (chosen !== undefined) {
target.push(chosen)
}
index++
}
return target
}
Expand All @@ -77,23 +81,27 @@ export function choose<T, U>(a: any, b?: any): any {
* Applies the given function to each element of the source array and concatenates all the results.
* @param mapping A function to transform elements of the input collection into collections that are concatenated.
*/
export function collect<T, U>(mapping: (item: T) => Iterable<U>): (source: T[]) => U[]
export function collect<T, U>(
mapping: (item: T, index: number) => Iterable<U>
): (source: T[]) => U[]
/**
* Applies the given function to each element of the source array and concatenates all the results.
* @param source The input collection.
* @param mapping A function to transform elements of the input collection into collections that are concatenated.
*/
export function collect<T, U>(source: T[], mapping: (item: T) => Iterable<U>): U[]
export function collect<T, U>(source: T[], mapping: (item: T, index: number) => Iterable<U>): U[]
export function collect<T, U>(a: any, b?: any): any {
const partial = typeof a === 'function'
const mapping: (item: T) => Iterable<U> = partial ? a : b
const mapping: (item: T, index: number) => Iterable<U> = partial ? a : b
function exec(source: T[]) {
const target = []
let index = 0
for (const item of source) {
const children = mapping(item)
const children = mapping(item, index)
for (const child of children) {
target.push(child)
}
index++
}
return target
}
Expand Down Expand Up @@ -142,25 +150,27 @@ export function concat<T>(sources: Iterable<T[]>): T[] {
* @param selector A function that transforms the array items into comparable keys.
* @param source The input collection.
*/
export function distinctBy<T, Key>(selector: (item: T) => Key): (source: T[]) => T[]
export function distinctBy<T, Key>(selector: (item: T, index: number) => Key): (source: T[]) => T[]
/**
* Returns an array that contains no duplicate entries according to the equality comparisons on
* the keys returned by the given key-generating function. If an element occurs multiple times in
* the sequence then the later occurrences are discarded.
* @param source The input collection.
* @param selector A function that transforms the array items into comparable keys.
*/
export function distinctBy<T, Key>(source: T[], selector: (item: T) => Key): T[]
export function distinctBy<T, Key>(source: T[], selector: (item: T, index: number) => Key): T[]
export function distinctBy<T, Key>(a: any, b?: any): any {
const partial = typeof a === 'function'
const selector: (item: T) => Key = partial ? a : b
const selector: (item: T, index: number) => Key = partial ? a : b
function exec(source: T[]): T[] {
const seen = new Map<Key, T>()
let index = 0
for (const item of source) {
const key = selector(item)
const key = selector(item, index)
if (!seen.has(key)) {
seen.set(key, item)
}
index++
}
return Array.from(seen.values())
}
Expand All @@ -172,16 +182,16 @@ export function distinctBy<T, Key>(a: any, b?: any): any {
* @param predicate A function to test each item of the input collection.
* @param source The input collection.
*/
export function exists<T>(predicate: (item: T) => boolean): (source: T[]) => boolean
export function exists<T>(predicate: (item: T, index: number) => boolean): (source: T[]) => boolean
/**
* Tests if any element of the array satisfies the given predicate.
* @param source The input collection.
* @param predicate A function to test each item of the input collection.
*/
export function exists<T>(source: T[], predicate: (item: T) => boolean): boolean
export function exists<T>(source: T[], predicate: (item: T, index: number) => boolean): boolean
export function exists<T>(a: any, b?: any): any {
const partial = typeof a === 'function'
const predicate: (item: T) => boolean = partial ? a : b
const predicate: (item: T, index: number) => boolean = partial ? a : b
function exec(source: T[]): boolean {
return source.some(predicate)
}
Expand All @@ -194,22 +204,24 @@ export function exists<T>(a: any, b?: any): any {
* @param source The input collection.
* @throws If no item is found matching the criteria of the predicate.
*/
export function get<T>(predicate: (item: T) => boolean): (source: T[]) => T
export function get<T>(predicate: (item: T, index: number) => boolean): (source: T[]) => T
/**
* Returns the first element for which the given function returns true.
* @param source The input collection.
* @param predicate A function to test whether an item in the collection should be returned.
* @throws If no item is found matching the criteria of the predicate.
*/
export function get<T>(source: T[], predicate: (item: T) => boolean): T
export function get<T>(source: T[], predicate: (item: T, index: number) => boolean): T
export function get<T>(a: any, b?: any): any {
const partial = typeof a === 'function'
const predicate: (item: T) => boolean = partial ? a : b
const predicate: (item: T, index: number) => boolean = partial ? a : b
function exec(source: T[]): T | undefined {
let index = 0
for (const item of source) {
if (predicate(item)) {
if (predicate(item, index)) {
return item
}
index++
}
throw new Error('Element not found matching criteria')
}
Expand All @@ -221,42 +233,53 @@ export function get<T>(a: any, b?: any): any {
* @param predicate A function to test whether an item in the collection should be returned.
* @param source The input collection.
*/
export function find<T>(predicate: (item: T) => boolean): (source: T[]) => T | undefined
export function find<T>(
predicate: (item: T, index: number) => boolean
): (source: T[]) => T | undefined
/**
* Returns the first element for which the given function returns true, otherwise undefined.
* @param source The input collection.
* @param predicate A function to test whether an item in the collection should be returned.
*/
export function find<T>(source: T[], predicate: (item: T) => boolean): T | undefined
export function find<T>(source: T[], predicate: (item: T, index: number) => boolean): T | undefined
export function find<T>(a: any, b?: any): any {
const partial = typeof a === 'function'
const predicate: (item: T) => boolean = partial ? a : b
const predicate: (item: T, index: number) => boolean = partial ? a : b
function exec(source: T[]): T | undefined {
let index = 0
for (const item of source) {
if (predicate(item)) {
if (predicate(item, index)) {
return item
}
index++
}
return undefined
}
return partial ? exec : exec(a)
}

export function groupBy<T, Key>(selector: (item: T) => Key): (source: T[]) => [Key, T[]][]
export function groupBy<T, Key>(source: T[], selector: (item: T) => Key): [Key, T[]][]
export function groupBy<T, Key>(
selector: (item: T, index: number) => Key
): (source: T[]) => [Key, T[]][]
export function groupBy<T, Key>(
source: T[],
selector: (item: T, index: number) => Key
): [Key, T[]][]
export function groupBy<T, Key>(a: any, b?: any): any {
const partial = typeof a === 'function'
const selector: (item: T) => Key = partial ? a : b
const selector: (item: T, index: number) => Key = partial ? a : b
function exec(source: T[]): [Key, T[]][] {
const groups = new Map<Key, T[]>()
let index = 0
for (const item of source) {
const key = selector(item)
const key = selector(item, index)
const group = groups.get(key)
if (group === undefined) {
groups.set(key, [item])
} else {
group.push(item)
}
index++
}
return Array.from(groups.entries())
}
Expand Down
55 changes: 55 additions & 0 deletions test/arrays.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('map', () => {
test('invoke', () => {
expect(Arrays.map([1, 2], x => x * 2)).toEqual([2, 4])
})
test('with index', () => {
expect(Arrays.map([1, 2], (x, index) => x * index)).toEqual([0, 2])
})
})

describe('filter', () => {
Expand All @@ -32,6 +35,9 @@ describe('filter', () => {
test('invoke', () => {
expect(Arrays.filter([1, 2, 3, 4], x => x % 2 === 0)).toEqual([2, 4])
})
test('with index', () => {
expect(Arrays.filter([1, 2, 3, 4], (x, index) => index % 2 === 0)).toEqual([1, 3])
})
})

describe('choose', () => {
Expand All @@ -43,6 +49,11 @@ describe('choose', () => {
test('invoke', () => {
expect(Arrays.choose([1, 2, 3], x => (x % 2 === 1 ? x * 2 : undefined))).toEqual([2, 6])
})
test('with index', () => {
expect(
Arrays.choose([1, 2, 3], (x, index) => (index % 2 === 0 ? x * index : undefined))
).toEqual([0, 6])
})
})

describe('collect', () => {
Expand All @@ -62,6 +73,13 @@ describe('collect', () => {
})
).toEqual([1, 1, 2, 2])
})
test('with index', () => {
expect(
Arrays.collect([1, 2], function(x, index) {
return [x, x + index]
})
).toEqual([1, 1, 2, 3])
})
})

describe('append', () => {
Expand Down Expand Up @@ -101,6 +119,19 @@ describe('distinctBy', () => {
)
).toEqual([{ name: 'amy', id: 1 }, { name: 'bob', id: 2 }, { name: 'cat', id: 3 }])
})
test('with index', () => {
expect(
pipe(
[
{ name: 'amy', id: 1 },
{ name: 'bob', id: 2 },
{ name: 'bob', id: 3 },
{ name: 'cat', id: 3 }
],
Arrays.distinctBy((x, index) => Math.floor(index / 2))
)
).toEqual([{ name: 'amy', id: 1 }, { name: 'bob', id: 3 }])
})
})

describe('exists', () => {
Expand All @@ -113,6 +144,9 @@ describe('exists', () => {
test('invoke', () => {
expect(Arrays.exists([1, 2], x => x === 1)).toEqual(true)
})
test('with index', () => {
expect(Arrays.exists([1, 2], (x, index) => index === 1)).toEqual(true)
})
})

describe('get', () => {
Expand All @@ -135,6 +169,11 @@ describe('get', () => {
Arrays.get([{ name: 'amy', id: 1 }, { name: 'bob', id: 2 }], x => x.name === 'bob')
).toEqual({ name: 'bob', id: 2 })
})
test('by index', () => {
expect(
Arrays.get([{ name: 'amy', id: 1 }, { name: 'bob', id: 2 }], (x, index) => index === 1)
).toEqual({ name: 'bob', id: 2 })
})
})

describe('find', () => {
Expand All @@ -157,6 +196,11 @@ describe('find', () => {
Arrays.find([{ name: 'amy', id: 1 }, { name: 'bob', id: 2 }], x => x.name === 'bob')
).toEqual({ name: 'bob', id: 2 })
})
test('by index', () => {
expect(
Arrays.find([{ name: 'amy', id: 1 }, { name: 'bob', id: 2 }], (x, index) => index === 1)
).toEqual({ name: 'bob', id: 2 })
})
})

describe('groupBy', () => {
Expand All @@ -181,6 +225,17 @@ describe('groupBy', () => {
[2, [{ name: 'bob', age: 2 }, { name: 'cat', age: 2 }]]
])
})
test('with index', () => {
expect(
Arrays.groupBy(
[{ name: 'amy', age: 1 }, { name: 'bob', age: 2 }, { name: 'cat', age: 2 }],
(x, index) => index % 2
)
).toEqual([
[0, [{ name: 'amy', age: 1 }, { name: 'cat', age: 2 }]],
[1, [{ name: 'bob', age: 2 }]]
])
})
})

describe('init', () => {
Expand Down

0 comments on commit c2ec011

Please sign in to comment.