Вот одна попытка. Возможно, я все еще что-то здесь упускаю, потому что я полностью игнорирую ваш параметр header
. Это как-то необходимо, или эта функциональность теперь захвачена ключами в change
объектах, сгенерированных вашими функциями обратного вызова?
// 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] }),
{}
)
)
// Main function
const finalFunction = (
{array: [headers, ...rows], ...rest},
callback,
changes = rows.map(r => transposeObj(callback(r).changes)),
allHeaders = [
...headers,
...changes
.flatMap (t => t .flatMap (Object.keys) )
.filter (k => !headers .includes (k))
.filter ((x, i, a) => a .indexOf (x) == i)
],
) => ({
array: [
allHeaders,
...rows .flatMap (
(row, i) => 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)
)
)
)
],
...rest
})
const data = {array: [["#", "FirstName", "LastName"], ["1", "tim", "foo"], ["2", "kim", "bar"]], more: 'stuff', goes: 'here'}
// Faked out to attmep
const callback1 = (row) => ({changes: {FirstName: [row[1][0].toUpperCase() + row[1].slice(1)]}})
const callback2 = (row) => ({changes: {FullName: [`${row[1]} ${row[2]}`]}})
const callback3 = (row) => ({changes: {Email: [`${row[1]}.${row[2]}@stackoverflow.com`,`${row[1]}my@gmail.com`],MailType: ["Work","Personal"]}})
console .log (finalFunction (data, callback1))
console .log (finalFunction (data, callback2))
console .log (finalFunction (data, callback3))
При этом используется вспомогательная функция transposeObj
, которая преобразует списки changes
в то, что я считаю более полезным. Это превращает это:
{
Email: ["tim.foo@stackoverflow.com", "timmy@gmail.com"],
MailType: ["Work", "Personal"]
}
в это:
[
{Email: "tim.foo@stackoverflow.com", MailType: "Work"},
{Email: "timmy@gmail.com", MailType: "Personal"}
]
Основная функция принимает ваш обратный вызов и объект данных с параметром array
, из которого извлекает headers
иМассивы rows
(а также отслеживание оставшихся свойств в rest
.) Он получает changes
, вызывая хелпер transposeObj
для свойства changes
, являющегося результатом вызова обратного вызова для каждой строки. Используя эти данные, он находит новые заголовки, получая все ключи в объектах changes
, удаляя все, что уже есть в массиве, а затем сокращая до набора уникальных значений. Затем он добавляет эти новые к существующим заголовкам для получения allHeaders
.
. В теле функции мы возвращаем новый объект, используя ...rest
для других параметров, и обновляем array
, запускаяс этим новым списком заголовков, а затем с плоским отображением rows
с функцией, которая берет каждый из этих транспонированных объектов и добавляет все его свойства к копии текущей строки, сопоставляя индексы с allHeaders
, чтобы поместить их в
Обратите внимание, что если ключи транспонированного объекта изменения уже существуют, этот метод просто обновит соответствующий индекс в выходных данных.
Мы тестировали выше с тремя фиктивными функциями обратного вызова, предназначенными дляедва охватить ваши примеры. Они не должны выглядеть как ваш производственный код.
Мы запускаем каждый из них отдельно для вашего ввода, генерируя три отдельных объекта результата. Обратите внимание, что это не изменяет ваши входные данные. Если вы хотите применить их последовательно, вы можете сделать что-то вроде:
const data1 = finalFunction (data, callback1)
console.log (data1, '-----------------------------------')
const data2 = finalFunction (data1, callback2)
console.log (data2, '-----------------------------------')
const data3 = finalFunction (data2, callback3)
console.log (data3, '-----------------------------------')
, чтобы получить результат, например:
{
array: [
["#", "FirstName", "LastName"],
["1", "Tim", "foo"],
["2", "Kim", "bar"]
],
more: "stuff",
goes: "here"
}
-----------------------------------
{
array: [
["#", "FirstName", "LastName", "FullName"],
["1", "Tim","foo", "Tim foo"],
["2", "Kim", "bar", "Kim bar"]
],
more: "stuff",
goes: "here"
}
-----------------------------------
{
array: [
["#", "FirstName", "LastName", "FullName", "Email", "MailType"],
["1", "Tim", "foo", "Tim foo", "Tim.foo@stackoverflow.com", "Work"],
["1", "Tim", "foo", "Tim foo", "Timmy@gmail.com", "Personal"],
["2", "Kim", "bar", "Kim bar", "Kim.bar@stackoverflow.com", "Work"],
["2", "Kim", "bar", "Kim bar", "Kimmy@gmail.com", "Personal"]
],
more: "stuff",
goes: "here"
}
-----------------------------------
Или, конечно, вы можете просто начать let data = ...
и затем выполните data = finalFunction(data, nextCallback)
в некотором цикле.
Эта функция сильно зависит от flatMap
, который доступен не во всех средах. Страница MDN предлагает альтернативы, если они вам нужны. Если вы по-прежнему используете Ramda, будет работать функция chain
.
Обновление
В вашем ответе было выбрано использование Ramda вместо этой необработанной версии ES6,Я думаю, что если вы собираетесь использовать Ramda, вы, вероятно, можете немного упростить его с более тяжелой дозой функций Ramda. Я предполагаю, что можно сделать больше, но я думаю, что это чище:
// Helper function
const transposeObj = (obj) =>
map (
(i) => reduce((a, [k, v]) => ({ ...a, [k]: v[i] }), {}, toPairs(obj)),
range (0, length (values (obj) [0]) )
)
// Main function
const finalFunction = (
{ array: [headers, ...rows], ...rest },
callback,
changes = map (pipe (callback, prop('changes'), transposeObj), rows),
allHeaders = uniq (concat (headers, chain (chain (keys), changes)))
) => ({
array: concat([allHeaders], chain(
(row) => map (
pipe (
toPairs,
reduce((r, [k, v]) => assocPath([indexOf(k, allHeaders)], v, r), row)
),
changes[indexOf(row, rows)]
),
rows
)),
...rest
})
const data = {array: [["#", "FirstName", "LastName"], ["1", "tim", "foo"], ["2", "kim", "bar"]], more: 'stuff', goes: 'here'}
// Faked out to attmep
const callback1 = (row) => ({changes: {FirstName: [row[1][0].toUpperCase() + row[1].slice(1)]}})
const callback2 = (row) => ({changes: {FullName: [`${row[1]} ${row[2]}`]}})
const callback3 = (row) => ({changes: {Email: [`${row[1]}.${row[2]}@stackoverflow.com`,`${row[1]}my@gmail.com`],MailType: ["Work","Personal"]}})
console .log (finalFunction (data, callback1))
console .log (finalFunction (data, callback2))
console .log (finalFunction (data, callback3))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {map, reduce, toPairs, range, length, values, pipe, prop, uniq, concat, chain, keys, assocPath, indexOf} = R </script>