Skip to content

Commit

Permalink
Added tests for cartesian.
Browse files Browse the repository at this point in the history
  • Loading branch information
dboikliev committed Jan 10, 2021
1 parent 5e86b07 commit 306c010
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 10 deletions.
35 changes: 28 additions & 7 deletions src/linq/iterables/cartesian.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { TcpSocketConnectOpts } from 'net'
import { elementsSymbol, ElementsWrapper } from '../element-wrapper'

export class Cartesian<TFirst, TSecond> implements ElementsWrapper<TFirst | TSecond> {
private cacheFirst = new Map<number, Promise<IteratorResult<TFirst>> | IteratorResult<TFirst>>()
private cacheSecond = new Map<number, Promise<IteratorResult<TSecond>> | IteratorResult<TSecond>>()

constructor(private readonly first: Iterable<TFirst> | AsyncIterable<TFirst>,
private readonly second: Iterable<TSecond> | AsyncIterable<TSecond>) {
private readonly second: Iterable<TSecond> | AsyncIterable<TSecond>,
private readonly preserveOrder: boolean = true) {
}

*[elementsSymbol](): IterableIterator<Iterable<TFirst | TSecond> | AsyncIterable<TFirst | TSecond>> {
Expand All @@ -19,10 +19,21 @@ export class Cartesian<TFirst, TSecond> implements ElementsWrapper<TFirst | TSec
throw Error('Expected @@iterator')
}

let firstIndex = 0
let secondIndex = 0
if (this.preserveOrder) {
for (const outer of this.first as Iterable<TFirst>) {
for (const inner of this.second as Iterable<TSecond>) {
yield [outer, inner]
}
}

return
}

const itFirst = this.first[Symbol.iterator]() as Iterator<TFirst>
const itSecond = this.second[Symbol.iterator]() as Iterator<TSecond>

let firstIndex = 0
let secondIndex = 0
let row = 0
let currentRow = 0
let currentCol = 0
Expand Down Expand Up @@ -86,11 +97,21 @@ export class Cartesian<TFirst, TSecond> implements ElementsWrapper<TFirst | TSec
throw Error('Expected @@iterator or @@asyncIterator')
}

let firstIndex = 0
let secondIndex = 0
if (this.preserveOrder) {
for await (const outer of this.first) {
for await (const inner of this.second) {
yield [outer, inner]
}
}

return
}

const itFirst = (typeof this.first[Symbol.asyncIterator] === 'function' && this.first[Symbol.asyncIterator]() || typeof this.first[Symbol.iterator] === 'function' && this.first[Symbol.iterator]()) as AsyncIterator<TFirst>
const itSecond = (typeof this.second[Symbol.asyncIterator] === 'function' && this.second[Symbol.asyncIterator]() || typeof this.second[Symbol.iterator] === 'function' && this.second[Symbol.iterator]()) as AsyncIterator<TSecond>


let firstIndex = 0
let secondIndex = 0
let row = 0
let currentRow = 0
let currentCol = 0
Expand Down
13 changes: 10 additions & 3 deletions src/linq/linqable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ export class Linqable<TSource> implements Iterable<TSource>, ElementsWrapper<TSo

/**
* Executes an action on each element of the sequence and yields the element.
* @param action - The action to execute on each element.
* @param action {FunctionF} - The action to execute on each element.
*/
tap(action: (element: TSource) => void): Linqable<TSource> {
return new Linqable(new Tap(this.elements, action))
Expand All @@ -771,8 +771,15 @@ export class Linqable<TSource> implements Iterable<TSource>, ElementsWrapper<TSo
return new Linqable(new Memoized(this.elements))
}

cartesian<TOther>(other: Iterable<TOther>): Linqable<[TSource, TOther]> {
return new Linqable(new Cartesian(this.elements, extractSync(other)))
/**
* Cartesion product of the linable with another sequence.
* For infinite sequences false should be passed for preserverOrder.
* @param {ITerable<TOther>} other - The other sequence.
* @param {boolean} preserveOrder - A flag indigating whether to traverse the sequences in order.
* @returns {Linqable<[TSource, TOther]>} A linqable of tuples representing elements of the cartesian product of the two sequences.
*/
cartesian<TOther>(other: Iterable<TOther>, preserveOrder = true): Linqable<[TSource, TOther]> {
return new Linqable(new Cartesian(this.elements, extractSync(other), preserveOrder))
}

toString(): string {
Expand Down
50 changes: 50 additions & 0 deletions test/cartesian.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect } from 'chai'
import { linq } from '../src/linq'

describe('cartesian', () => {
describe('sync', () => {
it('should return an empty seqeunce when one of the sequences is empty', () => {
expect(linq([]).cartesian([1, 2, 3, 4]).toArray()).to.be.empty
expect(linq([1, 2, 3, 4]).cartesian([]).toArray()).to.be.empty
expect(linq([]).cartesian([]).toArray()).to.be.empty
})

describe('preserveOrder = true', () => {
it('should return the cartesian product of two finite sequences preserving the order', () => {
const first = [1, 2, 3, 4]
const second = ['a', 'b', 'c']

const expected = []
for (const outer of first) {
for (const inner of second) {
expected.push([outer, inner])
}
}

expect(linq(first).cartesian(second).toArray()).to.have.deep.ordered.members(expected)
})

it('should return a cartesion product of the first element in the first sequence with elements of the seqeunce when the second is infinite', () => {
const first = [1, 2, 3, 4]
function* second() {
let i = 0
while (true) {
yield i++
}
}

const expected = []
let i = 1
for (const inner of second()) {
expected.push([1, inner])
if (i === 100) {
break
}
i++
}

expect(linq(first).cartesian(second()).take(100).toArray()).to.have.deep.ordered.members(expected)
})
})
})
})

0 comments on commit 306c010

Please sign in to comment.