I'm drawing a table dynamically from an array of objects, each object should have a left, center and right properties which are object as well.
I want to compare the object ahead to the objects that follows and find if any property is missing, if there are, I want to add rowSpan
with the missing difference.
For example: This
const arr =[
{Left:{},Center:{},Right:{}},
{Right:{}},
{Left:{},Right:{}},
{Left:{}},
{Left:{}}
]
Should look like
const arr =[
{Left:{rowSpan:2},Center:{rowSpan:5},Right:{}},
{Right:{}},
{Left:{},Right:{rowSpan:3}},
{Left:{}},
{Left:{}}
]
I also want add alignment accordingly, so if I added rowSpan
to an object key where that key can be still be found in the object that follows, I want to add align:bottom
to it, if it was the last key then I should add
align:top
and if it's the only key of it's kind I want to add align:center
For example:
const arr =[
{Left:{},Center:{},Right:{}},
{Right:{}},
{Left:{},Right:{}},
{Left:{},},
{Left:{}}
]
Should look like
const arr =[
{Left:{align:bottom},Center:{align:center},Right:{}},
{Right:{}},
{Left:{},Right:{align:top}},
{Left:{}},
{Left:{}}
]
CodePudding user response:
Provided below is one possible solution that achieves the desired objective.
Code Snippet
// helper method to transform the objects
// by adding row-span or align or both
const getNewValue = (k, v, idx, vmap, aLen, typ = 'both') => {
// here "k" can only be "Left", "Center" or "Right"
// get index of current element's "idx" on the "map" (parameter: "vmap")
const currKi = vmap[k].findIndex(i => i === idx);
// find where the next key (ie, Left, Center or Right) is present
// by using the "map"
const nextK = (
currKi >= 0
? currKi 1 < vmap[k].length
? vmap[k][currKi 1] // if "vmap" has the next Left, Center or Right
: aLen // if "vmap" doesn't have the next L, C or R
: -1 // if "vmap" doesn't have current "idx"
);
// determine the new alignment, if applicable
const align = (
vmap[k].length === 1
? 'center'
: nextK === aLen
? 'top'
: nextK >= 0
? 'bottom'
: undefined
);
return (
nextK >= 0 && nextK - idx > 1 // if row-span needs to be added
? {
...v,
...( // add row-span if "typ" is "both" or "span"
['both', 'span'].includes(typ)
? { rowSpan: nextK - idx } // the "number" is computed here
: {}
),
...( // add align if "typ" is "both" or "align"
['both', 'align'].includes(typ)
? { align } // populate the "align"
: {}
)
}
: v
);
};
// method to calculate row-span and/or align
const spanAndAlign = (arr, typ = 'both') => {
// first construct a map/dictionary
// Left: [0, 2, 3, 4]
// Center: [0]
// Right: [0, 1, 2]
// keys are L, C or R.
// values are arrays of index where each occurs in "arr" array
const lcrMap = arr.reduce(
(map, obj, idx) => {
Object.keys(map).forEach(k => {
if (k in obj) map[k].push(idx);
});
return map;
},
{Left: [], Center: [], Right: []}
);
// use the "lcrMap" to transform the given array
const res = arr.map(
(obj, idx) => (
Object.fromEntries(
Object.entries(obj).map(
([k, v]) => ([
k,
['Left', 'Center', 'Right'].includes(k) // only transform L, C or R
? getNewValue(k, v, idx, lcrMap, arr.length, typ)
: v
])
)
)
)
);
return res;
};
const origArr = [{
Left: {},
Center: {},
Right: {}
},
{
Right: {}
},
{
Left: {},
Right: {}
},
{
Left: {}
},
{
Left: {}
}
];
console.log('get Span Only: ', spanAndAlign(origArr, 'span'));
console.log('get Align Only: ', spanAndAlign(origArr, 'align'));
console.log('get Span & Alighn: ', spanAndAlign(origArr));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments provided in the above snippet
NOTE
This solution employs a number of JS concepts/features such as .reduce()
, .map()
, Object.fromEntries()
, Object.entries()
, .findIndex()
, .includes()
, ...
spread, ?:
ternary operator, etc.