sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
246 lines (195 loc) • 7.01 kB
JavaScript
// ----------------------------------------------------------------------------
const { isNumber, isDictionary, isArray } = require ('./type');
// ----------------------------------------------------------------------------
const keys = Object.keys; // eslint-disable-line padding-line-between-statements
const values = (x) => ((!isArray (x)) ? Object.values (x) : x); // don't copy arrays if they're already arrays
const index = (x) => new Set (values (x));
const extend = (...args) => Object.assign ({}, ...args); // NB: side-effect free
const clone = (x) => (isArray (x) ? Array.from (x) : extend (x)); // clone arrays or objects
// ----------------------------------------------------------------------------
module.exports = {
keys,
values,
extend,
clone,
index,
ordered: (x) => x, // a stub to keep assoc keys in order (in JS it does nothing, it's mostly for Python)
unique: (x) => Array.from (index (x)),
arrayConcat: (a, b) => a.concat (b),
// ------------------------------------------------------------------------
inArray (needle, haystack) {
return haystack.includes (needle);
},
toArray (object) {
return Object.values (object);
},
isEmpty (object) {
if (!object) {
return true;
}
return (Array.isArray (object) ? object : Object.keys (object)).length < 1;
},
// ------------------------------------------------------------------------
keysort (x, out = {}) {
for (const k of keys (x).sort ()) {
out[k] = x[k];
}
return out;
},
// ------------------------------------------------------------------------
/*
Accepts a map/array of objects and a key name to be used as an index:
array = [
{ someKey: 'value1', anotherKey: 'anotherValue1' },
{ someKey: 'value2', anotherKey: 'anotherValue2' },
{ someKey: 'value3', anotherKey: 'anotherValue3' },
]
key = 'someKey'
Returns a map:
{
value1: { someKey: 'value1', anotherKey: 'anotherValue1' },
value2: { someKey: 'value2', anotherKey: 'anotherValue2' },
value3: { someKey: 'value3', anotherKey: 'anotherValue3' },
}
*/
indexBy (x, k, out = {}) {
for (const v of values (x)) {
if (k in v) {
out[v[k]] = v;
}
}
return out;
},
// ------------------------------------------------------------------------
/*
Accepts a map/array of objects and a key name to be used as a grouping parameter:
array = [
{ someKey: 'value1', anotherKey: 'anotherValue1' },
{ someKey: 'value1', anotherKey: 'anotherValue2' },
{ someKey: 'value3', anotherKey: 'anotherValue3' },
]
key = 'someKey'
Returns a map:
{
value1: [
{ someKey: 'value1', anotherKey: 'anotherValue1' },
{ someKey: 'value1', anotherKey: 'anotherValue2' },
]
value3: [
{ someKey: 'value3', anotherKey: 'anotherValue3' }
],
}
*/
groupBy (x, k, out = {}) {
for (const v of values (x)) {
if (k in v) {
const p = v[k];
out[p] = out[p] || [];
out[p].push (v);
}
}
return out;
},
// ------------------------------------------------------------------------
/*
Accepts a map/array of objects, a key name and a key value to be used as a filter:
array = [
{ someKey: 'value1', anotherKey: 'anotherValue1' },
{ someKey: 'value2', anotherKey: 'anotherValue2' },
{ someKey: 'value3', anotherKey: 'anotherValue3' },
]
key = 'someKey'
value = 'value1'
Returns an array:
[
value1: { someKey: 'value1', anotherKey: 'anotherValue1' },
]
*/
filterBy (x, k, value = undefined, out = []) {
for (const v of values (x)) {
if (v[k] === value) {
out.push (v);
}
}
return out;
},
// ------------------------------------------------------------------------
// NB: MUTATES ARRAY!
sortBy: (array, key, descending = false, direction = descending ? -1 : 1) => array.sort ((a, b) => {
if (a[key] < b[key]) {
return -direction;
} else if (a[key] > b[key]) {
return direction;
} else {
return 0;
}
}),
sortBy2: (array, key1, key2, descending = false, direction = descending ? -1 : 1) => array.sort ((a, b) => {
if (a[key1] < b[key1]) {
return -direction;
} else if (a[key1] > b[key1]) {
return direction;
} else {
if (a[key2] < b[key2]) {
return -direction;
} else if (a[key2] > b[key2]) {
return direction;
} else {
return 0;
}
}
}),
// ------------------------------------------------------------------------
flatten: function flatten (x, out = []) {
for (const v of x) {
if (isArray (v)) {
flatten (v, out);
} else {
out.push (v);
}
}
return out;
},
// ------------------------------------------------------------------------
pluck: (x, k) => values (x).filter ((v) => k in v).map ((v) => v[k]),
// ------------------------------------------------------------------------
omit (x, ...args) {
if (!Array.isArray (x)) {
const out = clone (x);
for (const k of args) {
if (isArray (k)) { // omit (x, ['a', 'b'])
for (const kk of k) {
delete out[kk];
}
} else {
delete out[k]; // omit (x, 'a', 'b')
}
}
return out;
}
return x;
},
// ------------------------------------------------------------------------
sum (...xs) {
const ns = xs.filter (isNumber); // leave only numbers
return (ns.length > 0) ? ns.reduce ((a, b) => a + b, 0) : undefined;
},
// ------------------------------------------------------------------------
deepExtend: function deepExtend (...xs) {
let out = undefined;
for (const x of xs) {
if (isDictionary (x)) {
if (!isDictionary (out)) {
out = {};
}
for (const k in x) { // eslint-disable-line guard-for-in
out[k] = deepExtend (out[k], x[k]);
}
} else {
out = x;
}
}
return out;
},
};
;