JavaScript - Combine objects by summing values with matching keys

Below is the given array :

[
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": "1120"
  },
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": "12003"
  },
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Ruler",
    "Quantity": "34706"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Ruler",
    "Quantity": "6158"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": "3702"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Ruler",
    "Quantity": "158"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client2",
    "Product": "Pen",
    "Quantity": "80"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client2",
    "Product": "Ruler",
    "Quantity": "17200"
  },
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client3",
    "Product": "Pen",
    "Quantity": "393"
  },
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client3",
    "Product": "Pen",
    "Quantity": "4073"
  }
]

The goal is to combine objects with identical Date, Client, and Product.
This approach should be applicable for other scenarios such as merging object arrays based on matching Organisation && Client or just by Product.

The anticipated result will look like this :

[
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": "13123"
  },
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Ruler",
    "Quantity": "34706"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Ruler",
    "Quantity": "6316"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": "3702"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client2",
    "Product": "Pen",
    "Quantity": "80"
  },
  {
    "Date": "2019.07.07",
    "Organisation": "A",
    "Client": "Client2",
    "Product": "Ruler",
    "Quantity": "17200"
  },
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client3",
    "Product": "Pen",
    "Quantity": "4466"
  }
]

I attempted to implement a combination of reduce and findIndex, but it didn't yield the desired outcome of summing up similar values.

var data = obj.reduce((acc, v) => {
    const index = acc.findIndex(o => {
        return o["Date"] === v["Date"] &&
            o["Product"] === v["Product"] &&
            o["Client"] === v["Client"]
    });

    if (index >= 0) {
        var originalQty = Number(acc[index]["Quantity"]);
        originalQty += Number(v["Quantity"]);
        acc[index]["Quantity"] = originalQty.toString();
    }
    else {
        acc.push(v);
    }

    return acc;
}, []);

console.log(data);

return data;

UPDATE:
After some adjustments, I realized my code was correct; I just needed to modify the if statement to achieve the correct result.

if (index >= 0) {
                var originalQty = Number(acc[index]["Quantity"]);
                originalQty += Number(v["Quantity"]);
                acc[index]["Quantity"] = originalQty.toString();
            }  

Perhaps there was an issue with how my processed object handled numbers stored as strings, which affected the calculations. It's strange that simple addition wasn't working when dealing with stringified numbers...

Anyhow, thank you all for your quick responses!
Each response provided valuable insights!

Answer №1

To simplify the process, use reduce to work with an object first and then convert the values of that object into an array.

const arr = [{"Date":"2019.07.08","Organisation":"A","Client":"Client1","Product":"Pen","Quantity":"1120"},{"Date":"2019.07.08","Organisation":"A","Client":"Client1","Product":"Pen","Quantity":"12003"},{"Date":"2019.07.08","Organisation":"A","Client":"Client1","Product":"Ruler","Quantity":"34706"},{"Date":"2019.07.07","Organisation":"A","Client":"Client1","Product":"Ruler","Quantity":"6158"},{"Date":"2019.07.07","Organisation":"A","Client":"Client1","Product":"Pen","Quantity":"3702"},{"Date":"2019.07.07","Organisation":"A","Client":"Client1","Product":"Ruler","Quantity":"158"},{"Date":"2019.07.07","Organisation":"A","Client":"Client2","Product":"Pen","Quantity":"80"},{"Date":"2019.07.07","Organisation":"A","Client":"Client2","Product":"Ruler","Quantity":"17200"},{"Date":"2019.07.08","Organisation":"A","Client":"Client3","Product":"Pen","Quantity":"393"},{"Date":"2019.07.08","Organisation":"A","Client":"Client3","Product":"Pen","Quantity":"4073"}];

const res = Object.values(arr.reduce((a, { Date, Organisation, Client, Product, Quantity }) => {
  const s = `${Date}-${Client}-${Product}`;
  a[s] = a[s] || { Date, Organisation, Client, Product, Quantity: 0 };
  a[s].Quantity += +Quantity;
  return a;
}, {})).map(({ Quantity, ...r }) => ({ ...r, Quantity: Quantity.toString() }));

console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }

(To ensure accuracy, I added a map function to change Quantity back to a string after making calculations as numbers. This can be removed if needed, but note that the output will have numeric values for Quantity.)

Answer №2

To streamline the process, you can develop a function that accepts your data and an array of keys. Utilize the reduce method to create an object with combined values and then return Object.values, which will produce an array containing these merged values.

const data = [{"Date":"2019.07.08","Organisation":"A","Client":"Client1","Product":"Pen","Quantity":"1120"},{"Date":"2019.07.08","Organisation":"A","Client":"Client1","Product":"Pen","Quantity":"12003"},{"Date":"2019.07.08","Organisation":"A","Client":"Client1","Product":"Ruler","Quantity":"34706"},{"Date":"2019.07.07","Organisation":"A","Client":"Client1","Product":"Ruler","Quantity":"6158"},{"Date":"2019.07.07","Organisation":"A","Client":"Client1","Product":"Pen","Quantity":"3702"},{"Date":"2019.07.07","Organisation":"A","Client":"Client1","Product":"Ruler","Quantity":"158"},{"Date":"2019.07.07","Organisation":"A","Client":"Client2","Product":"Pen","Quantity":"80"},{"Date":"2019.07.07","Organisation":"A","Client":"Client2","Product":"Ruler","Quantity":"17200"},{"Date":"2019.07.08","Organisation":"A","Client":"Client3","Product":"Pen","Quantity":"393"},{"Date":"2019.07.08","Organisation":"A","Client":"Client3","Product":"Pen","Quantity":"4073"}]


function mergeData(data, keys) {
  const resultObj = data.reduce((accumulated, current) => {
    const key = keys.map(k => current[k]).join('|');
    if (!accumulated[key]) accumulated[key] = { ...current,
      Quantity: +current.Quantity
    };
    else accumulated[key].Quantity += +current.Quantity;
    return accumulated;
  }, {})

  return Object.values(resultObj)
}

console.log(mergeData(data, ['Date', 'Client', 'Product']))
console.log(mergeData(data, ['Organisation', 'Client']))

Answer №3

A custom function can be created to merge arrays based on specified keys. By setting up an accumulator object, unique combinations of properties from the keys array can be achieved.

function mergeArrays(arr, keys) {
  const group = arr.reduce((acc, o) => {
    const unique = keys.map(k => o[k]).join('|');
    if(acc[unique])
      acc[unique].Quantity += +o.Quantity;
    else
      acc[unique] = { ...o, Quantity: +o.Quantity };
    return acc;
  }, {})

 return Object.values(group)
}

In the example provided with properties ['Date', 'Client', 'Product'], this is how the resulting merged object will appear:

{
  "2019.07.08|Client1|Pen": {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": 13123
  },
  ...
}

To retrieve the values of the merged object as an array, utilize Object.values()

Include the following code snippet for reference:

// Insert array data here
const arr = [array elements here]

// Merge function call 
console.log(mergeArrays(arr, ['Date', 'Client', 'Product']))

Answer №4

Here is a simple solution that should work effectively:

  1. As the code iterates through, it constructs a map based on the unique keys' values.
  2. The Quantity attribute of the map is kept up-to-date as the iteration progresses.

var data = [
  {
    "Date": "2019.07.08",
    "Organisation": "A",
    "Client": "Client1",
    "Product": "Pen",
    "Quantity": "1120"
  },
  ...
]

var fieldsToUniqueOn = ["Client", "Product", "Organisation"]

document.getElementById("data").innerHTML = JSON.stringify(mergeData(data, fieldsToUniqueOn), null, 4);

function mergeData(data, uniqueOn) {
  var merged = {}
  data.forEach(item => {
      var key = fieldsToUniqueOn.map(field => item[field]).join("");
      if (merged[key] == null) {
          merged[key] = { ...item, Quantity: 0 }
      }
      merged[key]["Quantity"] += +item["Quantity"]
  });
  return Object.values(merged);
}
<pre id="data">
</pre>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Connect an Angular Service object to a Controller's Scope and keeping it in sync

I am facing an issue with the interaction between my EmployeeService and EmployeeController. The service contains a specific object which I bind to the controller's scope: app.controller('EmployeeController', function($scope, EmployeeServic ...

What is the best way to restrict the creation of objects in a JavaScript list?

Experiencing a fun challenge with HTML coding! Take a look at my ordered list in HTML: <ol id="myList"> <li>Tea</li> <li>Milk</li> <li>Water</li> </ol> <button onclick="myFunction()">Try it</ ...

Error in Typescript persists even after short-circuit evaluation is used

Kindly review the provided code sample type type1 = string[] | undefined; let variable1 : type1 = undefined; console.log(variable1 && variable1.length); Upon attempting to run this code in Typescript Playground, an error is generated stating Pro ...

Adjust the scroll bar to move in the reverse direction

I'm trying to modify the behavior of an overlay div that moves when scrolling. Currently, it works as expected, but I want the scroll bar to move in the opposite direction. For example, when I scroll the content to the right, I want the scroll bar to ...

Could someone break down for me the behavior exhibited within this method?

Hello there, I'm a beginner so please forgive me for any lack of knowledge. const example = { myFunction(){ console.log(this); }, myFunction2(){ function myFunction3(){ console.log(this) } return ...

There was a SyntaxError due to an unexpected token < appearing

I have been struggling for the past two weeks to find a solution to this issue, but without any success. I am using phpgrid from phpgrid.com, and I only encounter this error online when I upload it to the server. Each time I try to fill out a form with an ...

Store the names of uploaded files in the database for safekeeping

I store image files on a server. I aim to utilize these filename to save them in an existing database. The database name is "sob" and the table is "items". The field name is "PicturesFilenames", which is a medium text field. My goal is to save all file n ...

Developing a dynamic user interface using an Angular framework and populating it with

I am currently learning Angular (version 1) and facing an issue with my layout. I need to dynamically change the navigation based on the type of user that is logged in. To achieve this, I make an API request when the page loads to fetch the user object dat ...

Decoding Facebook JSON Game Invitation Information

Currently, I am utilizing Facebook's platform and have implemented a callback method that returns the JSON result as a string. This is an example of the format for the returned result: { "request": "420211088059698", "to": [ "100002669403922", ...

The fonts in node.js are not functioning as expected, without displaying any error messages

I'm having trouble implementing custom fonts on my website. Despite following the correct file structure, the fonts do not seem to be loading. My project files are organized in the following manner: https://gyazo.com/5ee766f030290e5b2fa42320cc39f10b ...

Having trouble with Autocomplete not entering cities into the form?

While experimenting with Google's API, I have encountered confusion regarding why cities like Staten Island, Brooklyn, Queens, and various others are not being placed into the form like other cities. According to Google's API, "locality" is suppo ...

Ensure the video frame stretches to fit the full width

I am struggling to make my video frame fill the entire width of the screen. Even though I have tried using CSS, the video container does not stretch to the full width as expected. https://i.sstatic.net/kxyE0.png How can I ensure that the video plays acro ...

What is the purpose of sorting an object using the sequence defined in an array?

Have you ever wondered how the sortWeekFunction function can rearrange an object based on a predefined array order? It may seem complex at first glance, but let's break down how this code actually works. const weeksArr = ['sunday', ' ...

The error TS2339 is indicating that there is no property called myProperty on the type SetStateAction<User>

I'm encountering a TypeScript error while working with React that's leaving me puzzled: <html>TS2339: Property 'subEnd' does not exist on type 'SetStateAction&lt;User&gt;'.<br/>Property 'subEnd' d ...

Can anyone help me with coloring Devanagiri diacritics in ReactJS?

I am currently working on a ReactJS project and I have come across an issue. I would like for the diacritic of a Devanagiri letter to be displayed in a different color than the letter it is attached to. For example: क + ी make की I was wondering ...

When the object contains multiple arrays, filter out the id and remove it when clicked

I am working with an object that contains multiple arrays and aiming to filter out the id of the item to be removed onClick. However, I have encountered an error while trying to implement this logic. The error message I received is filter is not a functio ...

Utilizing Angular.js to nest directives seamlessly without cluttering the markup

Expressing my query might pose some difficulty, but I appreciate your patience. I comprehend that in the realm of Angular.js, directives play a crucial role in driving dynamic markup. What was once achieved through jQuery can now be accomplished using dir ...

What is the correct way to convert a JArray into a list of strings?

I have a JArray saved in a variable of type object public object Errors { get; } This variable can store either of the following: Errors = {[ { "name": [ "Username &quot;admin&quot; has already been taken." ], ...

Utilizing PHP to dynamically load HTML forms and leveraging JQuery for form submissions

I am currently facing a dilemma that I am unsure how to approach. It seems that JQuery requires unique ID's in order to be called in the document ready function. I use PHP to read my MySQL table and print out HTML forms, each with a button that adds a ...

I am curious about the significance of the "=>" symbol within the Ionic framework

I utilized the documentation provided on the Ionic website to incorporate Firebase into my mobile application. this.firebase.getToken() .then(token => console.log(`The token is ${token}`)) // store the token server-side and utilize it for sending not ...