4

There is an async iterable

class Fasta {
    //read file line by line and yield a class based on every four lines
    constructor(path) {
        this.path = path
        const filestream = fs.createReadStream(this.path)
        if (this.path.match(/(\.fastq)|(\.fq)$/)) {
            this.filetype = 'fastq'
            this.handle = readline.createInterface({
                input: filestream,
                crlfDelay: Infinity
            })
        } else if (this.path.match(/\.gz$/)) {
            this.filetype = 'fqgz'
            this.handle = readline.createInterface({
                input: filestream.pipe(zlib.createGunzip()),
                crlfDelay: Infinity
            })
        }
    }
    async * [Symbol.asyncIterator]() {
        let counter = 0
        const rec = {0: '', 1: '', 2: '', 3: ''}
        for await (const line of this.handle) {
            if (counter < 3) {
                rec[counter] = line.trim()
                counter +=1
            } else if (counter == 3) {
                rec[counter] = line.trim()
                counter = 0
                yield new Dna(rec[0], rec[1], rec[3])
            }
        }
    }
}

and I want to do someting like this.

for await (const i of zip(new Fasta(args.filea), new Fasta(args.fileb))) {
// do the work
}

I've found several walkarouds here, but they all seem to be based on Array.map(). In this way I need to create an array to carry all the data. When the files are big, things go wrong.

I tried

async function * zip(fasta1, fasta2) {
    for await (const [i,j] of [fasta1, fasta2]) {
        yield [i,j]
    }
}

but it gave me a 'TypeError: .for is not iterable'.

Any help would be appreciated!

VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • 1
    The answers to this question might help you. https://stackoverflow.com/questions/4856717/javascript-equivalent-of-pythons-zip-function Do check it out. – Ghazi 360 Jun 03 '21 at 08:37
  • @Ghazi360 Thank you. But I need to zip an iterable class, not an array. Creating an array reading a large file would crash the app so these answers don't seem to work for me. – Quattro Zepplin Jun 03 '21 at 08:52
  • [iter-ops](https://github.com/vitaly-t/iter-ops) library can [zip](https://vitaly-t.github.io/iter-ops/index.html#zip) asynchronously ;) – vitaly-t Dec 25 '21 at 08:53

1 Answers1

2

Here's an async variation of my answer here:

async function* zip(...its) {

    async function* iter(it) {
        for await (let x of it)
            yield x
    }

    its = its.map(iter)

    while (true) {
        let rs = await Promise.all(its.map(it => it.next()))
        if (rs.some(r => r.done))
            return
        yield rs.map(r => r.value)
    }
}

// demo:

let delay = (a, n) => {
    console.log('begin', a)
    return new Promise(r => setTimeout(() => {
        console.log('resolved', a)
        r()
    }, n))
}

class Test {
    constructor(start) {
        this.start = start
    }

    async* [Symbol.asyncIterator]() {
        for (let i = 1; i < 10; i++) {
            await delay(this.start, Math.random() * 1000)
            yield this.start + i
        }
    }
}

async function main() {
    let iters = [
        new Test('a'),
        new Test('b'),
        new Test('c'),
    ]

    for await (let x of zip(...iters))
        console.log('ZIP', ...x)
}


main()
georg
  • 211,518
  • 52
  • 313
  • 390