Skip to content

Commit

Permalink
support iterable objects: String Map Set
Browse files Browse the repository at this point in the history
I added support for iterable objects using [Symbol.iterator],
also tests are written
  • Loading branch information
Yehor Khilchenko committed Mar 17, 2019
1 parent 8ad8ab6 commit ce1bb46
Show file tree
Hide file tree
Showing 11 changed files with 882 additions and 106 deletions.
178 changes: 119 additions & 59 deletions lib/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

const common = require('@metarhia/common');

const copy = name => {
switch (name) {
case 'Set':
return new Set();
case 'Map':
return new Map();
case 'Array':
return [];
case 'String':
return '';
}
return null;
};

// Asynchronous map (iterate parallel)
// items - <Array>, incoming
// fn - <Function>, to be executed for each value in the array
Expand All @@ -12,31 +26,47 @@ const common = require('@metarhia/common');
// done - <Function>, on done, optional
// err - <Error> | <null>
// result - <Array>
const map = (items, fn, done) => {
const map = (
// Asynchronous map (iterate parallel)
items, // array, incoming
fn, // function, (current, callback) => callback(err, value)
// to be executed for each value in the array
// current - current element being processed in the array
// callback - function(err, value)
done // function (optional), on done callback function(err, result)
) => {
done = done || common.emptyness;
const len = items.length;
if (!len) {
done(null, []);
const len = items.length || items.size;
const name = items.constructor.name;
let result = copy(name);
if (!len || !result) {
done(null, result);
return;
}
let errored = false;
let count = 0;
const result = new Array(len);
const data = items[Symbol.iterator]();

const next = (index, err, value) => {
const next = (err, value) => {
if (errored) return;
if (err) {
errored = true;
done(err);
return;
}
result[index] = value;
if (name === 'Array') result.push(value);
else if (name === 'Set') result.add(value);
else if (name === 'Map') result.set(value[0], value[1]);
else result += value;
count++;
if (count === len) done(null, result);
};

for (let i = 0; i < len; i++) {
fn(items[i], next.bind(null, i));
let i = 0;
while (i < len) {
const item = data.next();
fn(item.value, next);
i++;
}
};

Expand All @@ -59,8 +89,13 @@ const asyncMap = (items, fn, options = {}, done) => {
options = DEFAULT_OPTIONS;
}

if (!items.length) {
if (done) done(null, []);
const len = items.length || items.size;
const name = items.constructor.name;
let result = done ? copy(name) : null;
const data = items[Symbol.iterator]();

if (!len || !result) {
if (done) done(null, result);
return;
}

Expand All @@ -71,7 +106,6 @@ const asyncMap = (items, fn, options = {}, done) => {
let sum = 0;
let count = 0;

const result = done ? new Array(items.length) : null;
const ratio = percent / (1 - percent);

const countNumber = () => {
Expand All @@ -83,16 +117,21 @@ const asyncMap = (items, fn, options = {}, done) => {

const next = () => {
const itemsNumber = count ? countNumber() : min;
const iterMax = Math.min(items.length, itemsNumber + count);
const iterMax = Math.min(len, itemsNumber + count);

begin = Date.now();
for (; count < iterMax; count++) {
const itemResult = fn(items[count], count);
if (done) result[count] = itemResult;
const itemResult = fn(data.next().value, count);
if (done) {
if (name === 'Array') result.push(itemResult);
else if (name === 'Set') result.add(itemResult);
else if (name === 'Map') result.set(itemResult[0], itemResult[1]);
else result += itemResult;
}
}
sum += Date.now() - begin;

if (count < items.length) {
if (count < len) {
begin = Date.now();
setTimeout(next, 0);
} else if (done) {
Expand All @@ -115,39 +154,36 @@ const asyncMap = (items, fn, options = {}, done) => {
// result - <Array>
const filter = (items, fn, done) => {
done = done || common.emptyness;
const len = items.length;
const len = items.length || items.size;
const data = items[Symbol.iterator]();
const name = items.constructor.name;
let result = copy(name);

if (!len) {
done(null, []);
if (!len || !result) {
done(null, result);
return;
}

let count = 0;
let suitable = 0;
const data = new Array(len);
const rejected = Symbol('rejected');

const next = (index, err, accepted) => {
if (!accepted || err) {
data[index] = rejected;
} else {
data[index] = items[index];
suitable++;

const next = (value, err, accepted) => {
if (accepted && !err) {
if (name === 'Array') result.push(value);
else if (name === 'Set') result.add(value);
else if (name === 'Map') result.set(value[0], value[1]);
else result += value;
}
count++;
if (count === len) {
const result = new Array(suitable);
let pos = 0;
for (let i = 0; i < len; i++) {
const val = data[i];
if (val !== rejected) result[pos++] = val;
}
done(null, result);
}
};

for (let i = 0; i < len; i++) {
fn(items[i], next.bind(null, i));
let i = 0;
while (i < len) {
const item = data.next();
fn(item.value, next.bind(null, item.value));
i++;
}
};

Expand All @@ -173,7 +209,8 @@ const REDUCE_EMPTY_ARR =
// argument in first iteration
const reduce = (items, fn, done, initial) => {
done = done || common.emptyness;
const len = items.length;
const len = items.length || items.size || 0;
items = [...items];
const hasInitial = typeof initial !== 'undefined';

if (len === 0 && !hasInitial) {
Expand Down Expand Up @@ -231,7 +268,8 @@ const REDUCE_RIGHT_EMPTY_ARR =
// argument in first iteration
const reduceRight = (items, fn, done, initial) => {
done = done || common.emptyness;
const len = items.length;
const len = items.length || items.size || 0;
items = [...items];
const hasInitial = typeof initial !== 'undefined';

if (len === 0 && !hasInitial) {
Expand Down Expand Up @@ -276,10 +314,17 @@ const reduceRight = (items, fn, done, initial) => {
// done - <Function>, on done, optional
// err - <Error> | <null>
// items - <Array>
const each = (items, fn, done) => {
const each = (
// Asynchronous each (iterate in parallel)
items, // array, incoming
fn, // function, (value, callback) => callback(err)
// value - item from items array
// callback - callback function(err)
done // function (optional), on done callback function(err, items)
) => {
done = done || common.emptyness;
const len = items.length;
if (len === 0) {
const len = items.length || items.size;
if (!len) {
done(null, items);
return;
}
Expand All @@ -297,9 +342,7 @@ const each = (items, fn, done) => {
if (count === len) done(null);
};

for (let i = 0; i < len; i++) {
fn(items[i], next);
}
for (const item of items) fn(item, next);
};

// Asynchronous series
Expand All @@ -311,9 +354,17 @@ const each = (items, fn, done) => {
// done - <Function>, on done, optional
// err - <Error> | <null>
// items - <Array>
const series = (items, fn, done) => {
const series = (
// Asynchronous series
items, // array, incoming
fn, // function, (value, callback) => callback(err)
// value - item from items array
// callback - callback (err)
done // function (optional), on done callback (err, items)
) => {
done = done || common.emptyness;
const len = items.length;
const len = items.length || items.size;
const data = items[Symbol.iterator]();
let i = -1;

const next = () => {
Expand All @@ -322,7 +373,7 @@ const series = (items, fn, done) => {
done(null, items);
return;
}
fn(items[i], err => {
fn(data.next().value, err => {
if (err) {
done(err);
return;
Expand All @@ -345,15 +396,16 @@ const series = (items, fn, done) => {
// result - <any>
const find = (items, fn, done) => {
done = done || common.emptyness;
const len = items.length;
if (len === 0) {
const len = items.length || items.size;
const data = items[Symbol.iterator]();
if (!len) {
done();
return;
}
let finished = false;
const last = len - 1;

const next = (index, err, accepted) => {
const next = (index, item, err, accepted) => {
if (finished) return;
if (err) {
finished = true;
Expand All @@ -362,14 +414,15 @@ const find = (items, fn, done) => {
}
if (accepted) {
finished = true;
done(null, items[index]);
done(null, item);
return;
}
if (index === last) done(null);
};

for (let i = 0; i < len; i++) {
fn(items[i], next.bind(null, i));
const item = data.next().value;
fn(item, next.bind(null, i, item));
}
};

Expand All @@ -383,14 +436,21 @@ const find = (items, fn, done) => {
// done - <Function>, on done, optional
// err - <Error> | <null>
// result - <boolean>
const every = (items, fn, done) => {
const every = (
// Asynchronous every
items, // array, incoming
fn, // function, (value, callback) => callback(err, fits)
// value - item from items array
// callback - callback function(err, fits)
done // function, optional on done callback function(err, result)
) => {
done = done || common.emptyness;
if (items.length === 0) {
const len = items.length || items.size;
if (!len) {
done(null, true);
return;
}
let proceedItemsCount = 0;
const len = items.length;

const finish = (err, accepted) => {
if (!done) return;
Expand All @@ -402,7 +462,6 @@ const every = (items, fn, done) => {
proceedItemsCount++;
if (proceedItemsCount === len) done(null, true);
};

for (const item of items) fn(item, finish);
};

Expand All @@ -418,15 +477,16 @@ const every = (items, fn, done) => {
// result - <boolean>
const some = (items, fn, done) => {
done = done || common.emptyness;
const len = items.length;
const len = items.length || items.size;
const data = items[Symbol.iterator]();
let i = 0;

const next = () => {
if (i === len) {
done(null, false);
return;
}
fn(items[i], (err, accepted) => {
fn(data.next().value, (err, accepted) => {
if (err) {
done(err);
return;
Expand Down

0 comments on commit ce1bb46

Please sign in to comment.