Based on the help of @ScottSauyet I have been able to create a function resolving static and promise based callbacks for an initial data object.
Now I want to be able to pipe this data object through a series of callbacks, but run into trouble once I add multiple promises into the mix.
Current setup
// Libaries
const R = require('ramda');
const fetch = require('node-fetch');
const Promise = require('bluebird');
// Input
const data = {
array: [['#', 'FirstName', 'LastName'], ['1', 'tim', 'foo'], ['2', 'kim', 'bar']],
header: 'FirstName',
more: 'stuff',
goes: 'here'
};
// Static and Promise Resolver (with Helper Function)
const transposeObj = (obj, len = Object.values(obj)[0].length) =>
[...Array(len)].map((_, i) => Object.entries(obj).reduce((a, [k, v]) => ({ ...a, [k]: v[i] }), {}));
const mergeCallback = async ({ array: [headers, ...rows], header, ...rest }, callback) => {
const index = R.indexOf(header, headers);
const result = await Promise.map(rows, row => {
return callback(row[index]);
})
.then(x => ({ changes: x.map(v => transposeObj(v.changes)) }))
.then(({ changes }) => ({
allHeaders: R.flatten([
...headers,
R.chain(t => R.chain(Object.keys, t), [...changes])
.filter(k => !headers.includes(k))
.filter((x, i, a) => a.indexOf(x) == i)
]),
changes
}))
.then(({ changes, allHeaders }) => ({
resultRows: R.chain(
(row, i = R.indexOf(row, [...rows])) =>
changes[i].map(change =>
Object.entries(change).reduce(
(r, [k, v]) => [...r.slice(0, allHeaders.indexOf(k)), v, ...r.slice(allHeaders.indexOf(k) + 1)],
row.slice(0)
)
),
[...rows]
),
allHeaders
}))
.then(({ resultRows, allHeaders, array }) => ({
array: [allHeaders, ...resultRows],
header,
...rest
}));
return result;
};
// Example Callbacks and their services
const adapterPromise1 = async name => {
const response = await fetch(`https://api.abalin.net/get/getdate?name=${name}&calendar=us`).then(res => res.json());
return {
changes: {
nameday: R.pluck('day', response.results),
namemonth: R.pluck('month', response.results)
}
};
};
const servicePromise1 = input => mergeCallback(input, adapterPromise1);
const adapterPromise2 = async name => {
const response = await fetch(`https://api.genderize.io?name=${name}`).then(res => res.json());
return {
changes: {
gender: R.of(response.gender)
}
};
};
const servicePromise2 = input => mergeCallback(input, adapterPromise2);
const adapterStatic1 = name => ({ changes: { NameLength: R.of(R.length(name)) } });
const serviceStatic1 = input => mergeCallback(input, adapterStatic1);
Pipe Attempt
const result = R.pipe(
servicePromise1,
servicePromise2,
serviceStatic1
)(data);
// console.log(result); <<< preferred resolution method, but not working due to promise
result.then(console.log);
Expected Result
{ array:
[ [ '#',
'FirstName',
'LastName',
'nameday',
'namemonth',
'gender',
'NameLength' ],
[ '1', 'tim', 'foo', 24, 1, 'male', 3 ],
[ '1', 'tim', 'foo', 20, 6, 'male', 3 ],
[ '2', 'kim', 'bar', 8, 9, 'male', 3 ],
[ '2', 'kim', 'bar', 11, 10, 'male', 3 ] ],
header: 'FirstName',
more: 'stuff',
goes: 'here' }
Current result
Pipe works with any one service call, but as soon as I try to use two or more services, I receive the following error.
Cannot read property 'Symbol(Symbol.iterator)' of undefined
Any hint on how to get this to work would be greatly appreciated.