Kay Singian
Kay Singian

Reputation: 1391

Combine Items Pushed Inside Array Reduce

I have this code:

let result = Object.values(response.data.reduce((r,{ PO_NO, PO_LINE_NO, MATERIAL_NO, MATERIAL_NAME, PO_QTY, GRPO_QTY, GRPO_SHIPDATE }) => {
    r[PO_NO] = r[PO_NO] || { PO_NO, LINES: [] }
    r[PO_NO].LINES.push({ 
        LINE_NO: PO_LINE_NO, 
        PO_QTY: PO_QTY, 
        MATERIAL_NO: MATERIAL_NO, 
        MATERIAL_NAME: MATERIAL_NAME, 
        GRPO_QTY: GRPO_QTY, 
        GRPO_SHIPDATE: GRPO_SHIPDATE 
    })
    return r
},{}))

Which results into an array of nested objects. However, in the LINES.push() part, there are items that have the same line_no, material_no, material_name, and po_qty. The difference are grpo_qty and grpo_shipdate.

Is it possible to do remove the shipdate and get the sum of grpo_qty with the same line_no for every po_no so that I only have a single row per line_no of every po_no?

Example of response.data content:

  {
    "PO_NO": 35159,
    "LINES": [
      {
        "LINE_NO": 15,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 160000,
        "GRPO_SHIPDATE": "September, 21 2017 00:00:00"
      },
      {
        "LINE_NO": 15,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 320800,
        "GRPO_SHIPDATE": "October, 07 2017 00:00:00"
      },
      {
        "LINE_NO": 15,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 19200,
        "GRPO_SHIPDATE": "October, 20 2017 00:00:00"
      },
      {
        "LINE_NO": 16,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 60000,
        "GRPO_SHIPDATE": "September, 13 2017 00:00:00"
      },
      {
        "LINE_NO": 16,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 440000,
        "GRPO_SHIPDATE": "October, 20 2017 00:00:00"
      }
    ]
  },

Upvotes: 0

Views: 52

Answers (3)

StudioTime
StudioTime

Reputation: 23999

I would iterate over the array and sum the quantities:

const newLines = [],
    lineNumbers = [];
lines.forEach(line => {
    delete line.GRPO_SHIPDATE; /* delete shipdate */
    if (!lineNumbers.includes(line.LINE_NO)) {
        lineNumbers.push(line.LINE_NO); /* store current LINE_NO */
        newLines.push(line);
    } else {
        let toChange = newLines.filter(ln => { /* get current LINE_NO */
            return ln.LINE_NO === line.LINE_NO
        });
        toChange[0].GRPO_QTY = toChange[0].GRPO_QTY + line.GRPO_QTY;
    }
});

const lines = [{
        "LINE_NO": 15,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 160000,
        "GRPO_SHIPDATE": "September, 21 2017 00:00:00"
    },
    {
        "LINE_NO": 15,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 320800,
        "GRPO_SHIPDATE": "October, 07 2017 00:00:00"
    },
    {
        "LINE_NO": 15,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 19200,
        "GRPO_SHIPDATE": "October, 20 2017 00:00:00"
    },
    {
        "LINE_NO": 16,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 60000,
        "GRPO_SHIPDATE": "September, 13 2017 00:00:00"
    },
    {
        "LINE_NO": 16,
        "PO_QTY": 500000,
        "MATERIAL_NO": "130227",
        "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
        "GRPO_QTY": 440000,
        "GRPO_SHIPDATE": "October, 20 2017 00:00:00"
    }
]
const newLines = [],
    lineNumbers = [];
lines.forEach(line => {
    delete line.GRPO_SHIPDATE; /* delete shipdate */
    if (!lineNumbers.includes(line.LINE_NO)) {
        lineNumbers.push(line.LINE_NO); /* store current LINE_NO */
        newLines.push(line);
    } else {
        let toChange = newLines.filter(ln => { /* get current LINE_NO */
            return ln.LINE_NO === line.LINE_NO
        });
        toChange[0].GRPO_QTY = toChange[0].GRPO_QTY + line.GRPO_QTY;
    }
});

console.log(newLines)

Upvotes: 0

Mulan
Mulan

Reputation: 135367

Writing functions matchLine and combineLine help us break groupPoLines down into an easier task - note, each function here will NOT mutate its inputs

const matchLine = (a, b) =>
  a.LINE_NO === b.LINE_NO
    && a.PO_QTY === b.PO_QTY
    && a.MATERIAL_NO === b.MATERIAL_NO

const combineLine = ({ GRPO_SHIPDATE:_, ...a }, b) =>
  ({ ...a, GRPO_QTY: a.GRPO_QTY + b.GRPO_QTY })

const groupPoLines = ({ LINES, ...po }) => ({
  ...po,
  LINES: LINES.reduce ((r, x) => {
      const i = r.findIndex (y => matchLine (x, y))
      if (i < 0)
        return [ ...r, x ]
      else
        return Object.assign (r, { [i]: combineLine (r[i], x) })
    }, [])
})

console.log (groupPoLines (data))
// { PO_NO: 35159,
//   LINES: 
//   [ { LINE_NO: 15,
//       PO_QTY: 500000,
//       MATERIAL_NO: '130227',
//       MATERIAL_NAME: 'T3-0381 Base Mold  φ10 M2',
//       GRPO_QTY: 500000 },
//     { LINE_NO: 16,
//       PO_QTY: 500000,
//       MATERIAL_NO: '130227',
//       MATERIAL_NAME: 'T3-0381 Base Mold  φ10 M2',
//       GRPO_QTY: 500000 } ] }

If you have an array of POs, you can simply map our new function over it

console.log (poList.map (po => groupPoLines (po)))
// [ { PO_NO: 1, LINES: [ ... ] }, { PO_NO: 2, LINES: [ ... ] } ] 

Expand the snippet to verify it works

const data = {
  "PO_NO": 35159,
  "LINES": [
    {
      "LINE_NO": 15,
      "PO_QTY": 500000,
      "MATERIAL_NO": "130227",
      "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
      "GRPO_QTY": 160000,
      "GRPO_SHIPDATE": "September, 21 2017 00:00:00"
    },
    {
      "LINE_NO": 15,
      "PO_QTY": 500000,
      "MATERIAL_NO": "130227",
      "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
      "GRPO_QTY": 320800,
      "GRPO_SHIPDATE": "October, 07 2017 00:00:00"
    },
    {
      "LINE_NO": 15,
      "PO_QTY": 500000,
      "MATERIAL_NO": "130227",
      "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
      "GRPO_QTY": 19200,
      "GRPO_SHIPDATE": "October, 20 2017 00:00:00"
    },
    {
      "LINE_NO": 16,
      "PO_QTY": 500000,
      "MATERIAL_NO": "130227",
      "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
      "GRPO_QTY": 60000,
      "GRPO_SHIPDATE": "September, 13 2017 00:00:00"
    },
    {
      "LINE_NO": 16,
      "PO_QTY": 500000,
      "MATERIAL_NO": "130227",
      "MATERIAL_NAME": "T3-0381 Base Mold  φ10 M2",
      "GRPO_QTY": 440000,
      "GRPO_SHIPDATE": "October, 20 2017 00:00:00"
    }
  ]
}

const matchLine = (a, b) =>
  a.LINE_NO === b.LINE_NO
    && a.PO_QTY === b.PO_QTY
    && a.MATERIAL_NO === b.MATERIAL_NO
    
const combineLine = ({ GRPO_SHIPDATE:_, ...a }, b) =>
  ({ ...a, GRPO_QTY: a.GRPO_QTY + b.GRPO_QTY })

const groupPoLines = ({ LINES, ...po }) => ({
  ...po,
  LINES: LINES.reduce ((r, x) => {
      const i = r.findIndex (y => matchLine (x, y))
      if (i < 0)
        return [ ...r, x ]
      else
        return Object.assign (r, { [i]: combineLine (r[i], x)})
    }, [])
})

console.log (groupPoLines (data))

console.log ('---')

console.log ([data, data, data].map(d => groupPoLines (d)))

Upvotes: 1

Nicolas Gehlert
Nicolas Gehlert

Reputation: 3253

Well I think you can just iterate over each entry and call a reduce function on the lines array. In this function you can create a unique key with properties that stay the same and then sum up the grpo_qty values.

It could look something like this

result.forEach(entry => {
    entry.LINES = Object.values(entry.LINES.reduce((result, current) => {
        const uniqueKey = `${current.LINE_NO}-${current.MATERIAL_NO}-${current.PO_QTY}-${current.MATERIAL_NAME}`;
        if (!result[uniqueKey]) {
            result[uniqueKey] = {
                LINE_NO: current.LINE_NO,
                PO_QTY: current.PO_QTY,
                MATERIAL_NO: current.MATERIAL_NO,
                MATERIAL_NAME: current.MATERIAL_NAME,
                GRPO_QTY: 0,
            };
        }
        result[uniqueKey].GRPO_QTY += current.GRPO_QTY;

        return result;
    }, {}));
});

Maybe you can provide a jsfiddle so it is easier to test.

You could also imagine to do this in your original reduce. Creating the unique key there and also sum up the values. Then you later just need to convert the object back to an array.

Theoretically it would also be possible to directly write everything in an array and find the existing entry with array.find(), but personally I'd suggest using one of the reduce. It is a lot cleaner, more performant and easier to read

Upvotes: 0

Related Questions