My Objective is to create a fixed column array object of size 4 columns so that I can form a 2d array in the following format which I can use to print to a pdf later. The solution is working fine. But need help in figuring out if there is a better way to implement this.
let input = [{
id:123,
quantity: 4,
value: "xxxx"
},
{
id:234,
quantity: 11,
value: "xxxx"
},
{
id:345,
quantity: 1,
value: "xxxx"
}]
output = [
[ objHeader, objValue, objValue, objValue ],
[ objValue, null, null, null ],
[ objHeader1, objValue1, objValue1, objValue1 ],
[ objValue1, objValue1, objValue1, objValue1 ],
[ objValue1, objValue1, objValue1, objValue1 ],
[ objHeader2, objValue2, null, null ]
]
where objHeader = {
id:123,
header:true,
quantity: 4,
value: xxxx
}
objValue = {
id:123,
header:false,
quantity: 4,
value: xxxx
}
objValue is basically input[0].id repeated by the factor present in quantity
objValue1 is input[1].id repeated by the factor present in quantity and so on
Code that is working now
let input = [
{
id: 123,
quantity: 4,
value: "x1xxx"
},
{
id: 234,
quantity: 11,
value: "x2xxx"
},
{
id: 345,
quantity: 1,
value: "x3xxx"
}
];
class RowRecord {
constructor(rowsize = 4) {
this.items = Array(rowsize).fill(null);
this.rowSize = rowsize;
}
push(item) {
const currentSize = this.size();
// console.log(currentSize);
if (currentSize < this.rowSize) {
// replace 1 items with null based on index
this.items.splice(currentSize, 1, item);
}
}
get() {
return this.items;
}
size() {
return this.items.filter(item => item !== null).length;
}
}
function getRecords() {
const records = [];
let rows = new RowRecord(); // fill default
// create records
for (let i = 0; i < input.length; i++) {
// check for pending rows
if (i !== 0 && rows.size() > 0) {
records.push(rows);
}
// initiate new row
rows = new RowRecord();
const item = input[i];
const quantity = parseInt(item.quantity, 10);
const value = item.value;
// +1 for printing the label for each product
for (let j = 0; j <= quantity; j++) {
if (j === 0) {
// title label
rows.push({
id: item.id,
header: true,
quantity,
value
});
} else {
if (rows.size() === 4) {
records.push(rows);
rows = new RowRecord();
}
rows.push({
id: item.id,
header: false,
quantity,
value
});
}
}
}
// push any pending rows
records.push(rows);
return records;
}
console.log(getRecords());
2 Answers 2
You can also use Array.prototype.reduce to generate the fixed size 2d array
.
const input = [{
id: 123, quantity: 4, value: "x1xxx"
}, {
id: 234, quantity: 11, value: "x2xxx"
}, {
id: 345, quantity: 1, value: "x3xxx"
}];
function getNewRecords(rowSize = 4) {
return input.reduce((records, current) => {
let items = [];
Array.from({
length: current.quantity + 1 // +1 for header column
}).forEach((_, index) => {
const record = {
header: index === 0,
id: current.id,
quantity: current.quantity,
value: current.value
}
if (items.length === rowSize) {
records.push({ items, rowSize });
items = [];
}
items.push(record);
});
if (items.length < rowSize) {
items.push(...Array(rowSize - items.length).fill(null));
}
records.push({ items, rowSize });
return records;
}, []);
}
console.log(getNewRecords());
Separating your code out into functions could help readability. I tried writing the functions to each handle a single requirement of your data. Is rowSize
really necessary, given that you have access to row.length
?
function makeOutputHeader(input_obj) {
return {...input_obj,header:true}
}
function* makeOutputValues(input_obj) {
for (let i=0;i<input_obj.quantity;++i)
yield {...input_obj,header:false}
}
function makeCells(input_obj) {
return [
makeOutputHeader(input_obj),
...makeOutputValues(input_obj)
];
}
function range(count) {
return Array.from({length:count}).map((_,i)=>i);
}
function makeGrid(cells,width) {
const height = Math.ceil(cells.length/width);
const rows = range(height).map(
row=>range(width).map(
col=>cells[row*width+col] || null
)
)
return rows
}
function rowsForInputObj(header,width) {
const cells = makeCells(header);
return makeGrid(cells,width);
}
function getRecords(input,width) {
const rows_for_headers = input.map(
inputObj=>rowsForInputObj(inputObj,width)
);
const all_rows = rows_for_headers.reduce((a,b)=>a.concat(b),[]);
return all_rows;
}
console.log(getRecords(input,4));