-
Notifications
You must be signed in to change notification settings - Fork 35
support Iterable objects, such as Map, Set, String #415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
ce1bb46 to
4596e10
Compare
|
It is better to use |
lib/array.js
Outdated
| if (done) result[count] = itemResult; | ||
| const itemResult = fn(data.next().value, count); | ||
| if (done) { | ||
| if (name === 'Array') result.push(itemResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, an object with these types as keys and an appropriate mapping functions as values looks prettier for purpose like this. I suppose, that this piece of code will not be relevant after using common.iter, however just pointing it out for future reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I ask you a question?
If I replace my const data = items[Symbol.iterator]();
with const data = common.iter(items); I will get almost the same, and I have no need to rewrite code where I use data.next().value.
So, Is it what I am expected to do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I've missconcepted this, you may need here to use a constructor name to add element to result. So it still make sense, AFAIS, to move this conditional statement to a separate function or to make an mapping object described previously, because it is used at least four times in this file, kind of boilerplate code, if I'm not mistaken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mille-nium What do you think about this solution with separate function?
const push = (name, result, value) => {
switch (name) {
case 'Set':
return result.add(value);
case 'Map':
return result.set(value[0], value[1]);
case 'Array':
return result.push(value);
case 'String':
return result += value;
}
return null;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer not using switch-case here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I ask you again?
If I separate push function prettier code says that I should change my let result to const result How can I solute this? Can I turn off this option, or what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, If I give string into object pushValue, I won't concat my string with string += value
If I do something like this, It will work
return;
}
if (name === 'String') result += value;
else push(name, result, value);
count++;
if (count === len) done(null, result);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder, if you could simply use common.Iterator.map() and others methods and then just common.Iterator.collectTo()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be, I will check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose, this was actually meant by @o-rumiantsev
test/array.every.js
Outdated
|
|
||
| metatests.test('every', test => { | ||
| metatests.test('every / array', test => { | ||
| const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be moved to a separate variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly should I move to separate variable?
const data = [...]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]. I don't mind different name though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
You can also use |
|
@o-rumiantsev I have one misunderstanding with you. It is said that |
|
Now I have one inaccuracy. If I call method ArrayChain.prototype.fetch = function(fn) {
this.chain.push({ op: 'fetch', fn });
this.execute();
return this;
}; |
|
@SemenchenkoVitaliy @belochub I noticed one interesting detail, I can be wrong so correct me if I am. metatests.test('for chain after fetch', test => {
metasync
.for([1, 2, 3, 4])
.map((item, cb) => process.nextTick(() => cb(null, item * item)))
.filter((item, cb) => process.nextTick(() => cb(null, item > 5)))
.fetch((error, result, resume) => {
test.error(error);
test.strictSame(result, [9, 16]);
process.nextTick(() => resume(null, result));
})
.filter((item, cb) => {
process.nextTick(() => cb(null, item > 10));
})
.map((item, cb) => {
process.nextTick(() => cb(null, --item));
})
.fetch((error, result) => {
test.error(error);
test.strictSame(result, [15]);
test.end();
});
});I will get async functions, but it doesn't work as should, I will get errors. (it is master) |
|
In last commit I have described my problem with |
test/array.every.js
Outdated
|
|
||
| metatests.test('every', test => { | ||
| metatests.test('every / array', test => { | ||
| const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still relevant.
lib/array.js
Outdated
| const promise = | ||
| isArray || items.constructor.name === 'String' | ||
| ? data.toArray() | ||
| : data.collectTo(items.constructor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After consulting with @belochub, we agreed that we should return an Iterator object if items is not an Array.
eabbd1d to
1070a5f
Compare
nechaido
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Majority of comments apply to every function.
lib/array.js
Outdated
| // result - <Array> | ||
| // initial - <any>, optional value to be used as first | ||
| // argument in first iteration | ||
| const reduceRight = (items, fn, done, initial) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reduceRight should probably be implemented on AsyncIterator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
f477971 to
dc722a5
Compare
lib/array.js
Outdated
| // result - <Array> | ||
| // initial - <any>, optional value to be used as first | ||
| // argument in first iteration | ||
| const reduceRight = (items, fn, done, initial) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
test/array.each.js
Outdated
| ); | ||
| }); | ||
|
|
||
| // metatests.test('each with error', test => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this is commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because now each works parallel and this callback callback(eachError); will not cause the stopping of processing other items.
What should I do with this test? rewrite for checking if other elements were processed?
test/chain.js
Outdated
| test.strictSame(result, [15]); | ||
| test.end(); | ||
| }); | ||
| // .filter((item, cb) => process.nextTick(() => cb(null, item < 4))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This problem with fetch after fetch wasn't solved by me. Ask @nechaido.
3c1d416 to
a65fd11
Compare
lib/array.js
Outdated
| .parallel(item => promisify(fn)(item)) | ||
| .then(res => { | ||
| const accepted = res.every(predicateResult => predicateResult); | ||
| done(null, accepted); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| done(null, accepted); | |
| done(null, res.every(e => e); |
lib/async-iterator.js
Outdated
|
|
||
| class ReverseIterator extends AsyncIterator { | ||
| constructor(base) { | ||
| const newBase = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be done on the first next invocation. base is always AsyncInterotor.
lib/array.js
Outdated
| } | ||
| const isArray = Array.isArray(items); | ||
| asyncIter(items) | ||
| .parallel(item => promisify(fn)(item)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| .parallel(item => promisify(fn)(item)) | |
| .parallel(promisify(fn)) |
lib/array.js
Outdated
| next(); | ||
| const isArray = Array.isArray(items); | ||
| const iter = asyncIter(items) | ||
| .map(item => promisify(fn)(item)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| .map(item => promisify(fn)(item)) | |
| .map(promisify(fn)) |
test/array.find.js
Outdated
|
|
||
| metatests.test('with array without element which is searching', test => { | ||
| const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; | ||
| metatests.test('find without element which is searching', test => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| metatests.test('find without element which is searching', test => { | |
| metatests.test('find without the desired element', test => { |
|
|
||
| metasync.reduce( | ||
| arr, | ||
| (prev, cur, callback) => process.nextTick(() => callback(null, prev + cur)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| (prev, cur, callback) => process.nextTick(() => callback(null, prev + cur)), | |
| test.mustNotCall(), |
lib/array.js
Outdated
| done(null, []); | ||
| // result - <Iterable> | ||
| const map = (items, fn, done = common.emptiness) => { | ||
| if (!items[Symbol.iterator]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, Iterable can also contain Symbol.asyncIterator and do not contain Symbol.iterator.
const asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
(async function() {
for await (const i of asyncIterable) {
console.log(i);
}
})();There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIU we can omit this check because AsyncIterator constructor will make them.
lib/array.js
Outdated
|
|
||
| if (!items.length) { | ||
| if (done) done(null, []); | ||
| if (!items[Symbol.iterator]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
lib/array.js
Outdated
| if (!len) { | ||
| done(null, []); | ||
| const filter = (items, fn, done = common.emptiness) => { | ||
| if (!items[Symbol.iterator]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
42ebb93 to
304b2b7
Compare
|
ping @belochub. |
nechaido
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, split this PR into two commits: one that adds support for Iterables and the one that "updates" tests.
lib/array.js
Outdated
| .parallel(async item => [await promisify(fn)(item), item]) | ||
| .then(res => { | ||
| const filtered = res | ||
| .filter(([predicateResult]) => predicateResult) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
common.Iterator.filterMap ?
|
|
||
| metasync.asyncMap( | ||
| arr, | ||
| item => item * 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to leave this test the way it is, to ensure that synchronous functions are still supported.
test/array.every.js
Outdated
| metatests.test('every with error', test => { | ||
| const data = [1, 2, 3]; | ||
| const everyErr = new Error('Every error'); | ||
| const everyError = new Error('Every error'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer not to rename variables.
test/array.every.js
Outdated
| metatests.test('every with empty array', test => | ||
| strictSameResult([], true, test, () => test.end()) | ||
| ); | ||
| metatests.test('every with empty', test => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need to change this?
ee7985b to
4f027ba
Compare
4f027ba to
8cf8ca8
Compare
8cf8ca8 to
d713492
Compare
| // result - <Iterable> | ||
| const map = (items, fn, done = common.emptiness) => { | ||
| const isArray = Array.isArray(items); | ||
| asyncIter(items) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think wrapping everything in AsyncIterator and additionally using util.promisify() adds unnecessary overhead, that can be avoided by just rewriting the original functions with iterators in mind. This also won't require any changes to metasync.AsyncIterator. If we decide to keep it the way it is in this PR we should probably at least benchmark this variant to make sure this doesn't lead to significant performance regressions.
/cc @tshemsedinov
I added support for iterable objects using [Symbol.iterator],
also tests are written