How to Combine JSON Objects But Keep Unchanged Data
Combining two JSON objects together is a typical assignment in JavaScript application development. Tableau offers several different methods for merging two objects together while preserving existing data, whether it be API responses, user settings, application configuration, or stored data.
The problem is that a plain old merge can cause the existing values to be overwritten, relevant information to be lost, or odd behavior when doing merges on properties of objects. When working with merging JSON objects, without discarding existing data, it is crucial to understand the way the JavaScript execution environment handles object attributes, and what merging technique you should use.
In this guide, you will find out how to merge two JSON objects with the `spread operator, Object.assign(), custom merge functions, and deep merging techniques. Practical examples, common pitfalls, and tips for safeguarding existing data during object combining are also included.
What is it When JSON Objects are Merged?
When merging JSON objects, one is combining properties of more than two or more objects into one object.
For instance, you have two objects with user information in each:
const user = {
name: "Alex",
age: 28
};
const additionalInfo = {
email: "alex@example.com",
country: "USA"
};
You wish to merge these objects with the intention of not changing any of its current attributes.
With JavaScript, they can be combined with the spread operator:
const mergedUser = {
...user,
...additionalInfo
};
console.log(mergedUser);
Output:
{
"name": "Alex",
"age": 28,
"email": "alex@example.com",
"country": "USA"
}
Both objects have properties which are added to another object.
Things get interesting when both objects have properties with the same name or include other objects (called “nested objects”). Learning about these scenarios is important for preventing data loss.
Why Is Data Lost When JSON Objects are Merged?
Data loss occurs when a single object overwrites another or when a merge operation simply replaces an entire nested object as opposed to merging properties.
Consider this example:
const existingData = {
name: "Alex",
age: 28,
email: "alex@example.com"
};
const newData = {
name: "John",
country: "USA"
};
const mergedData = {
...existingData,
...newData
};
console.log(mergedData);
Output:
{
"name": "John",
"age": 28,
"email": "alex@example.com",
"country": "USA"
}
The name property in the second object has the same property name as in the first object, so the value of the original name property ( "Alex") has been replaced with the value of the second object’s name property ( "John").
It’s normal to see it in a JavaScript code. When spreading multiple objects into a new object, any later properties replace any previous properties with the same key.
Other common reasons why you may lose data unexpectedly:
- Replacement of an existing object rather than merging objects’ attributes.
- Shallow merge on objects with nested objects.
- Overwriting meaningful information with zero, null or empty strings/dates.
- To alter an original object, without meaning to.
- Expecting that merging JSON data will take care of arrays and so on.
Depending on what you want, it’s recommended to either use new values, keep old values, or merge properties inside of properties.
The following is an example of how a JSON Object can be combined with another using the spread operator.
Actually one of the easiest ways to merge objects in modern JavaScript is using the spread operator (…).
Combines multiple objects into a new object that has enumerable properties.
Take two objects and combine them.Example: Combine 2 objects.
const existingData = {
name: "Alex",
age: 28,
city: "New York"
};
const newData = {
email: "alex@example.com",
country: "USA"
};
const mergedData = {
...existingData,
...newData
};
console.log(mergedData);
Output:
{
"name": "Alex",
"age": 28,
"city": "New York",
"email": "alex@example.com",
"country": "USA"
}
All properties are moved over – with different keys across both objects, all properties are moved.
How to not override previous values
When an element does exist, and you would like to preserve it, position the existing object last, or remove it from the array altogether, if you would like it to take priority.
const existingData = {
name: "Alex",
age: 28
};
const newData = {
name: "John",
country: "USA"
};
const mergedData = {
...newData,
...existingData
};
console.log(mergedData);
Output:
{
"name": "Alex",
"age": 28,
"country": "USA"
}
The original name is not changed, since the data item at the end is named as existingData.
Caution: It’s a shallow merge with the spread operator. Not designed to automatically join nested objects, require another method with data with multiple depths.
To merge two objects together into a new one, use the Object.assign() function.
Another method inbuilt in JavaScript for merging objects is Object.assign().
It is a method for copying enumerable own properties of one or more objects to a target object.
Basic Example
const existingData = {
name: "Alex",
age: 28
};
const newData = {
email: "alex@example.com",
country: "USA"
};
const mergedData = Object.assign(
{},
existingData,
newData
);
console.log(mergedData);
Output:
{
"name": "Alex",
"age": 28,
"email": "alex@example.com",
"country": "USA"
}
The empty object {} is the target. When used, it will prevent the changes to be made on existingData directly by using Object.assign() object.
Remember that Object.assign() preserves existing values.
Priority of which values take precedence can be controlled by changing the order of the source objects.
const existingData = {
name: "Alex",
age: 28
};
const newData = {
name: "John",
email: "alex@example.com"
};
const mergedData = Object.assign(
{},
newData,
existingData
);
console.log(mergedData);
Output:
{
"name": "Alex",
"age": 28,
"email": "alex@example.com"
}
The last object that is copied is the one you already have, so this means that if two objects have properties with the same names, the values in the latter object will be copied.
Object Spread vs Object.assign.
Both ways do the shallow merge, the syntax is different.
- Spread operator: Usually more concise and readable for creating new objects.
- Object.assign(): Convenient for adding properties to a target object.
- The “mutation” method takes the form of “Object.assign(target, source)”, modifying its target. Allows to pass the target as { } to prevent changes to the original source objects.
- Nested objects: They don’t perform a deep merge.
The spread operator is a great way to start, for the most basic merges with objects.
(a) How many distinct ways are there to combine JSON objects?(a) How many ways can we combine JSON Objects?
One of the largest causes of having existing data overwritten accidentally by developers is called nested objects.
Take into account the following objects:
const existingData = {
name: "Alex",
settings: {
theme: "dark",
notifications: true
}
};
const newData = {
settings: {
language: "en"
}
};
Then combine them using the spread argument:
const mergedData = {
...existingData,
...newData
};
console.log(mergedData);
Output:
{
"name": "Alex",
"settings": {
"language": "en"
}
}
The result does not contain original properties like the “theme” or the “notifications” properties.
Why? Since the entire property value of the second object assigned to the left side overwrites the first object’s value of the same property.
Description: To merge Nested Objects separately.Description: merged separately the Nested Objects.
A solution to help preserve nested properties is to spread the nested objects separately.
const mergedData = {
...existingData,
...newData,
settings: {
...existingData.settings,
...newData.settings
}
};
console.log(mergedData);
Output:
{
"name": "Alex",
"settings": {
"theme": "dark",
"notifications": true,
"language": "en"
}
}
This means that now three settings are stored.
This is useful when it is certain that the properties of the object being searched contain nested objects.
With multiple levels of nesting in your JSON data though, you will find it hard to merge all the property nesting values. If that’s the case, it’s probably better to use a deep merge function with recursion.
Write JavaScript deep merge function.
A deep merge for objects will also merge them recursively, so that nested objects will not be overwritten.
The following example is a simple deep merge function for plain objects:
function deepMerge(target, source) {
const result = { ...target };
for (const key of Object.keys(source)) {
const sourceValue = source[key];
const targetValue = result[key];
if (
isPlainObject(targetValue) &&
isPlainObject(sourceValue)
) {
result[key] = deepMerge(
targetValue,
sourceValue
);
} else {
result[key] = sourceValue;
}
}
return result;
}
function isPlainObject(value) {
if (
value === null ||
typeof value !== "object"
) {
return false;
}
const prototype = Object.getPrototypeOf(value);
return (
prototype === Object.prototype ||
prototype === null
);
}
This function will create a new top level object and recursively merge value if both corresponding properties are “plain objects”.
DeepMergeNestedData: Merge two sets of deeply nested data,taining distinct keys.
const existingData = {
user: {
name: "Alex",
preferences: {
theme: "dark",
notifications: true
}
}
};
const newData = {
user: {
preferences: {
language: "en"
}
}
};
const mergedData = deepMerge(
existingData,
newData
);
console.log(mergedData);
Output:
{
"user": {
"name": "Alex",
"preferences": {
"theme": "dark",
"notifications": true,
"language": "en"
}
}
}
The original Name, Theme and Notification preference are not changed.
New language preference is added along with other language preferences, instead of replacing them.
Important limitations: This is a simple deep merge of plain objects. Arrays are not merged and special objects like dates, maps, class instances must be treated separately. This implementation does not also support circular references.
If application data is complicated, apply a fully-tested deep merge memory melder library or specify a merging rule for each data type.
When combined with new data that contains Undefined, save the existing values also.
In some scenarios an incoming item may have properties that are null.
For example:
const existingData = {
name: "Alex",
age: 28,
email: "alex@example.com"
};
const newData = {
name: undefined,
age: 30
};
const mergedData = {
...existingData,
...newData
};
console.log(mergedData);
Previously the existing name was preserved like this:
It can be an issue if, for example, API responses or partial updates include undefined properties.
During a Merger exclude Undefined Values.
An undefined value can be filtered prior to spreading the incoming object onto it.
const filteredData = Object.fromEntries(
Object.entries(newData).filter(
([key, value]) => value !== undefined
)
);
const mergedData = {
...existingData,
...filteredData
};
console.log(mergedData);
Output:
{
"name": "Alex",
"age": 30,
"email": "alex@example.com"
}
The existing name and e-mail is not affected and the age is updated.
This example only works at top level properties. In case undefined values are present in objects within objects you have to apply the same principle on a recursive manner or implement an appropriate deep merge implementation.
Also note that null is not undefined. This means that undefined values are not filtered out so that null` cannot replace a value.
a. Combine JSON Objects without altering the original data:
When dealing with application state or API data and configuration objects, care should be taken to prevent unnecessary mutations.
Consider this example:
const existingData = {
name: "Alex",
age: 28
};
const newData = {
country: "USA"
};
const mergedData = Object.assign(
existingData,
newData
);
This will change the ‘existingData’ since it is targeted on Object.assign.
To keep the original object, use the spread operator:
const mergedData = {
...existingData,
...newData
};
Alternatively:
const mergedData = Object.assign(
{},
existingData,
newData
);
Either alternative is the construction of a new top level object.
A shallow copy, however, does not isolate any nested objects. If the original and result object share a nested object, changes to that nested object can impact either of the objects.
For nested data use immutable updates at appropriate levels or deep copying strategy consistent with data in use.
Answers to the question of whether it is a good idea to merge JSON strings safely.
JSON is a serial format and JavaScript objects are in-memory values. When data is JSON strings, parse the strings prior to merging.
For example:
const json1 = '{"name":"Alex","age":28}';
const json2 = '{"email":"alex@example.com","country":"USA"}';
const object1 = JSON.parse(json1);
const object2 = JSON.parse(json2);
const mergedObject = {
...object1,
...object2
};
const mergedJson = JSON.stringify(
mergedObject,
null,
2
);
console.log(mergedJson);
Output:
{
"name": "Alex",
"age": 28,
"email": "alex@example.com",
"country": "USA"
}
The procedure will happen in three steps:
- Using a function such as JSON.parse(), validate and parse each valid JSON string.
- Combine the resulting objects in JavaScript in the right way!
- Use the method,
JSON.stringify(), to convert the merged object to its JSON again.
Handle errors in JSON Input.
If the JSON string is malformed, then JSON.parse() throws SyntaxError.
This is a safe way to do it with a try...catch:
function parseJson(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error(
"Invalid JSON input:",
error.message
);
return null;
}
}
Before merging, make sure that parsing is successful, and that the structure of the values is as expected.
Don’t expect all valid JSON values to be objects. Besides objects, JSON supports arrays, strings, numbers, booleans and `null.
8. Ways of dealing with Arrays in JSON Object Association
Arrays are objects that have special rules; they are different from plain objects.
Let’s say you want to compare two objects that have arrays:
const existingData = {
name: "Alex",
hobbies: ["reading", "cycling"]
};
const newData = {
hobbies: ["cooking"]
};
const mergedData = {
...existingData,
...newData
};
console.log(mergedData);
Output:
{
"name": "Alex",
"hobbies": ["cooking"]
}
Because the array taking priority gets preference, the original hobbies are replaced.
To keep all the elements in place in the array, they may be concatenated explicitly:
const mergedData = {
...existingData,
...newData,
hobbies: [
...existingData.hobbies,
...newData.hobbies
]
};
console.log(mergedData);
Output:
{
"name": "Alex",
"hobbies": [
"reading",
"cycling",
"cooking"
]
}
This is valid when it is justifiable to reduce all the elements of an array.
If the values in the array are distinct, then use a Set:
const hobbies = [
...new Set([
...existingData.hobbies,
...newData.hobbies
])
];
When working with arrays of objects, e.g. products or users, it might be required that you don’t just concatenate the arrays but merge by a unique identifier.
Always choose the action to be performed on the arrays: Replace, Concatenate, Deduplicate, Merge, by identifier. No one rule fits all applications.
9. Select the appropriate merge method for your data
Combining JSON objects is most easily done depending on the requirements.
| Preserves unrelated properties | Handles nested objects | Modifies source object | |
|---|---|---|---|
| < | No | No, shallow only | No, at the top level |
Object.assign({}, ...) | Yes | No, shallow only | No, at the top level |
| Manual nested merge | Yes if done correctly, Only Levels specified on the source side if done correctly, No if copies correctly. | ||
| Custom deep merge | Supported data (depends on implementation) | Depends on implementation | |
| Concatenation of arrays | Requires the context of the merge | Not automatically generated | No, when adding a new array |
When things are small and have nothing to do with one another, the spread operator is often just as good.
For nested settings, or for configuration data, consider explicit nested merging or a proven deep merge implementation.
For arrays, specify if they should “Behave” or “Append”.
For partially API updates, think about why you might want undefined and null and empty strings and missing properties to mean different things.
10. Best Practices for Preventing Data Loss
Correctly merging any objects isn’t as easy as selecting a JavaScript method. Also there should be explicit rules, as to which values remain or not.
Understand Property Precedence
In a spread-based merge, if the two objects have the same key, they will typically be overwritten in favor of the latter object involved in the merge.
const merged = {
...existingData,
...newData
};
Use this order when it is desired that a new value overwrite the previous one.
Reverse order if it is desired to give existing values the first override.
Be aware to not mistake Missing Properties with Null Values.
Missing property, undefined property and null property is different.
Before using a merge function, determine which of these values to use instead of any current values.
Avoid Unnecessary Mutation
When you wish to keep the original data, create a new object.
This can come in handy when updating a state in a framework like React or updating data that goes across multiple functions.
Validate Incoming Data
Ensure that data that flows in is structured in the way you expected it to be before consolidating.
API responses might contain, for instance, an unanticipated string instead of an object. Just because something is valid JSON doesn’t mean it complies with your app’s needs.
Test Nested Objects and Arrays.
Try your merge logic for matching keys, missing keys, nested objects, arrays, null and undefined keys.
These test cases can expose issues which may be trivially missed by just using simple test cases.
Merging using complex libraries works fine.
Consider using a well maintained and documented library when an application requires the merging of complicated data structures.
Before employing it with potentially untrusted input, study its behaviour with arrays, null values, special objects and prototype-related dangers.
Commonly asked questions (FAQs)
In JS, how to combine two JSON objects together?
The JSON strings can be converted to objects in JavaScript and the spread operator can be used:
const merged = {
...JSON.parse(json1),
...JSON.parse(json2)
};
This does a shallow merge. Properties in the second object overwrite the properties in the first that are the same.
2. Does the spread operator retain the data?
Yes, it is keeping info about properties that are not overwritten by the other sources afterwards. If both objects have the same key however, the later one will take precedence. Each row of nested objects can be completely replaced.
3) How can you combine nested JSON objects with the loss-less of properties?
Concur the nested objects separately:
const merged = {
...existingData,
...newData,
settings: {
...existingData.settings,
...newData.settings
}
};
There are multiple ways of nesting: do a recursive deep merge function that supports your data structure.
4. What does shallow merge vs deep merge refer to?
A shallow merge is a merge that replaces nested objects but keeps merging top level ones if they have the same key. Deep merge – recursively merges supported nested objects keeping properties that aren’t overwritten.
5. Does Object.assign() over write the exist properties?
Yes. If the source objects have the same property, the latest source’s property value will take precedence. Utilize an empty object for this target, so you don’t taint your source object.
If some old values were present in the source JSON data and the corresponding value now has the undefined value, how can one avoid discarding the old value?
Exclude undefined properties when you merge:
const filtered = Object.fromEntries(
Object.entries(newData).filter(
([key, value]) => value !== undefined
)
);
const merged = {
...existingData,
...filtered
};
This is an example of an application filter that filters top-level properties. If the same behaviour is desired at all levels, then the objects are recursive.
7. Is there any way to merge JSON object without changing the object itself?
Yes. The spread operator forms a new top level object:
const merged = {
...existingData,
...newData
};
Note that objects that are nested in the original object can be shared with the created object.
8. How can I concatenate arrays in json objects?
The spread operator can be used to concatenate arrays:
const mergedArray = [
...existingArray,
...newArray
];
Depending on your application’s requirements, you might want to concatenate, deduplicate, replace or merge by an identifier.
10. What is the difference between normally defining an object property value and using the Object.defineProperty API?
The number of characters (known as code points) that are produced by the No. function JSON.stringify() to represent a given JavaScript value. If you need JSON text, you have to first merge the objects and then stringify the result.
According to you what is the safe way to merge JSON objects?
Use the spread operator and proper order of properties for simple objects. If the data is deeply nested, then map each section via explicit nested merges, or apply a well-tested nested merge solution. Ensure that data is coming in correctly and set up the rules for what to do if data is duplicated, if it is an undefined value or a null value, and if it is in an array.
Key Takeaways
Those are just some of the ways to call objects to merge in JavaScript—albeit without the need to alter the top-level objects.
- If the keys are the same, subsequent properties will overwrite previous properties.
- Object.assign() also does a shallow merge of the objects and changes the source object.
- The Nested objects have to be handled specially to have an impact on properties which are already there.
- Deep merge is a function that can recursively merge nested plain objects, and may still return undefined results based on how this function is defined.
- Unless you filter out undefined values they will overwrite existing values!
- Each array requires an explicit method of handling the relationship, like a replacement, the addition of contents to the end, deduplication, or a merge by identifier, for example.
- When combining, make sure to ‘parse‘’ JSON strings back first and ‘stringify’ them back later if they are needed as a JSON text.
- Verify data and fail safe cases when using merged data in production.
When we merge multiple JSON objects, the primary objective will be to merge data without overwriting any significant value. After learning the precedence of properties, the difference of shallow and deep merging, and how undefined and arrays work, you can pick a dependable way of merging in your java script application.