-
Notifications
You must be signed in to change notification settings - Fork 35
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
support Iterable objects, such as Map, Set, String #415
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
@@ -60,7 +60,7 @@ metatests.test('every with two-element arrays', test => | |||
) | |||
); | |||
|
|||
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
@@ -60,7 +60,7 @@ metatests.test('every with two-element arrays', test => | |||
) | |||
); | |||
|
|||
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
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
@@ -231,16 +191,17 @@ const REDUCE_RIGHT_EMPTY_ARR = | |||
// 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
@@ -231,16 +191,17 @@ const REDUCE_RIGHT_EMPTY_ARR = | |||
// 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.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
@@ -213,6 +221,16 @@ class MapIterator extends AsyncIterator { | |||
} | |||
} | |||
|
|||
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 => { |
}); | ||
|
||
metatests.test('reduce without initial and with single-element array', test => { | ||
metatests.test('reduce single-element array without initial', test => { | ||
const arr = [2]; | ||
|
||
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. |
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
?
const arr = [1, 2, 3]; | ||
const expectedArr = [2, 4, 6]; | ||
|
||
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
@@ -26,23 +26,23 @@ const fewStrictSameResult = (inOutPairs, test) => { | |||
|
|||
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