I want to create a dynamic function that can simplify work with array-transforming callbacks in order to fill and expand 2d Array.
Outlining the challenge
I would like to create a function like this
finalFunction({ array, header, ...args }, callbackFunctionToTransformArray)
Restrictions
- The given array is always a 2d array
- The header is supplied as a string to be passed onto the callbackFunction
- The callback function always has to return a "changes" Object containing the headers as Keys. The values for each key contain an array of the values to be inserted
which can pass all three scenarios given the following set input parameters (part of an input object):
{
array = [
["#","FirstName","LastName"]
["1","tim","foo"],
["2","kim","bar"]
],
header: "FirstName",
...args
}
Important
The challenges is not in the creation of the callback functions, but rather in the creation of the "finalFunction".
Scenario 1: Transforming existing Array without expansion
// return for the second row of the array
callback1 => {
changes: {
FirstName: ["Tim"]
}
};
// return for the third row of the array
callback1 => {
changes: {
FirstName: ["Kim"]
}
};
finalFunction({ array, header, ...args }, callback1)
should return
{
array: [
["#","FirstName","LastName"]
["1","Tim","foo"],
["2","Kim","bar"]
],
header: "FirstName",
...args
}
Scenario 2: Transforming existing Array with horizontal expansion
// return given for the second row
callback2 => {
changes: {
FullName: ["Tim Foo"]
}
};
// return given for the third row
callback2 => {
changes: {
FullName: ["Kim Bar"]
}
};
finalFunction({ array, header, ...args }, callback2)
should return
{
array: [
["#","FirstName","LastName","FullName"]
["1","Tim","foo","Tim Foo"],
["2","Kim","bar","Kim Bar"]
],
header: "FirstName",
...args
}
Scenario 3: Transforming existing Array with vertical and horizontal expansion
// return given for the second row
callback3 => {
changes: {
"Email": ["[email protected]","[email protected]"],
"MailType": ["Work","Personal"]
}
};
// return given for the third row
callback3 => {
changes: {
"Email": ["[email protected]","[email protected]"],
"MailType": ["Work","Personal"]
}
};
finalFunction({ array, header, ...args }, callback3)
should return
{
array: [
["#","FirstName","LastName","Email","MailType"]
["1","Tim","foo","[email protected]","Work"],
["1","Tim","foo","[email protected]","Personal"],
["2","Kim","bar","[email protected]","Work"],
["2","Kim","bar","[email protected]","Personal"]
],
header: "FirstName",
...args
}
Current progress
The wonderful @Scott Sauyet has helped me create a merging function between a 2d array and a changes object:
const addInputToArray = ({ array, changes, ...rest}) => ({
array: Object .entries (changes) .reduce ((a, [k, vs], _, __, index = array [0] .indexOf (k)) =>
vs.reduce(
(a, v, i) =>
(i + 1) in a
? update ((i + 1), update (index, v, a [i + 1] ), a)
: concat (a, [update (index, v, map (always (''), array [0]) )] ),
a),
array
),
...rest
})
This works great for scenario #1. However, I can't seem to get this solution to autocreate headers if they are not part of the original array.
I have however made progress on the Vertical expansion described in scenario 3.
const expandVertically = ({ array, header, index = array[0].indexOf(header), ...args }, callback) => ({
array: array.reduce((a, v, i) => {
if (i === 0) {
a.push(v);
} else {
const arrayBlock = R.repeat(v, callback(v[index]).length);
arrayBlock.unshift(array[0]);
const result = addInputToArray({
changes: callback(v[index]).changes,
array: arrayBlock
}).array;
result.shift();
result.map(x => a.push(x));
}
return a;
}, []),
header,
...args
})
In my mind, the newly created logic would have to.
- Call the callback Function in order to retrieve the entries that could be missing for the first Header row
- Add missing keys of "changes" object to the header row
- Reduce over the array skipping the first row
- Always assume an arrayblock (as it's fine if an arrayblock only has the length one, which would cover scenarios #1 and #2)
- Assure that the arrayblock length doesn't need "length" parameter to be supplied by the callback, but rather be captured from the arraylength of values supplied for each key in the "changes" obj
Current Challenges
- The current solution of vertical expansion requires the callback to provide a "length" parameter in it's result in order to get the correct number of repetitions for each source row.
- The current function to merge the "changes" with the sourceArray does not autocreate new Headers if they couldn't be found in the first row of the source array.
I feel that this is doable and it would provide great benefits to the current project I am working on, as it applies a standardized interface for all array-fillings/expansions.
However I feel stuck, particularly on how to cover all 3 scenarios in a single function.
Any ideas or insights would be greatly appreciated.
{..., changes: {FirstName: 'Tim'}, ...}would you expect to get...['#2', 'Bim', 'bar'], ...? Why not, it just repeated the obvious transformation of the first letter:char * 2 - 148? That converts't'to'T', so the equivalent'k'to'B'should be fine, right? Or why not guess that the second transformation is justname => name + 'Foo'? Trying to guess what you want would be ad hoc and too frequently wrong. You would have to pass functions.