5
\$\begingroup\$

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());

\$\endgroup\$
1
\$\begingroup\$

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());

\$\endgroup\$
1
\$\begingroup\$

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));
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.