Update a nested object in JavaScript - javascript

This is the original data
const data = {
"field1": {
"name": 'Anuv',
"marks": {
"eng": 43,
"hindi": 23
},
"age": 21
},
"field2": {
"school": 'DAV'
}
}
I am trying to update the name
const updatedValue = {
"field1": {
"name": "Anuv Gupta"
}
}
This is the expected data. It should have all the field and the updated name value as well.
const expectedData = {
"field1": {
"name": 'Anuv Gupta',
"marks": {
"eng": 43,
"hindi": 23
},
"age": 21
},
"field2": {
"school": 'DAV'
}
}
I have tried using these
expectedData = Object.assign({}, data, updatedValue)
as well as
expectedData = { ...data, ...updatedValue },
both of them returns this object
const obj = {
"field1": {
"name": 'Anuv Gupta',
},
"field2": {
"school": 'DAV'
}
}
How do I fix this and get the expectedData object?

If you don't care about mutating your original data you can just do:
data.field1.name = 'Anuv Gupta';
console.log(data);
If you don't want your original data to mutate, just clone it first and do the new value assignment:
const dataClone = structuredClone(data);
dataClone.field1.name = 'Anuv Gupta';
console.log(dataClone);
Edit:
Like others suggested you can also achieve that by spreading your data object into a new one like so:
const newData = {
...data,
field1: {
...data.field1,
name: 'Anuv Gupta',
}
}
It works but it is not mutation proof as it only shallow clones the original data object - You can read more about shallow vs deep clone in this great blog post. if you care about not mutating your original data, I would use the second option I mentioned in my answer

Avi's answer is good. Just to add one more method that is strictly immutable, you could do the following:
const expectedData = {
...data,
"field1": {
...data.field1,
"name": 'Anuv Gupta',
}
}

You can access the name property directly:
data.field1.name = "new value"
If you're trying to avoid mutating the original data obj you can try:
data2 = Object.assign({}, data);
data2.field1.name = "new value"

just change it directly using an Object property
data.field1.name = 'anuv Gupta'
and don't use quotes for object keys just do this and it works just fine
const obj = {
field1: {
name: 'Anuv Gupta',
},
field2: {
"school": 'DAV'
}
}

Related

Is there way to rearange objects in array that the value becomes the key?

I'm stuck on this type of situation where the values of the object is changed to a different value. Is there way to shift a value to a key or would simply deleting and adding be better? I tried to loop to see which of the keys overlap in value and using the if statement and conditions i tried adding or deleting using Array methods. However, since the inter data is an object i am sruggling to find the right methods or even the process. I also tried using a function to insert the data and pushing to a new empty array that is returned from the function.
If I have objects in an array like so:
const data = [
{
"date": "12/22",
"treatment": "nausea",
"count": 2
},
{
"date": "12/23",
"treatment": "cold",
"count": 3
},
{
"date": "12/22",
"treatment": "cold",
"count": 2
}
];
and wanting to change the data like so:
const newData = [
{
"date": "12/22",
"cold": 2
"nausea": 2,
},
{
"date": "12/23",
"cold": 3
}
];
try this code using loop and reduce and every time add to new array
const data = [
{
"date": "12/22",
"treatment": "nausea",
"count": 2
},
{
"date": "12/23",
"treatment": "cold",
"count": 3
},
{
"date": "12/22",
"treatment": "cold",
"count": 2
}
];
const newData = [];
const dataByDate = data.reduce((acc, curr) => {
if (!acc[curr.date]) {
acc[curr.date] = { date: curr.date };
}
acc[curr.date][curr.treatment] = curr.count;
return acc;
}, {});
for (let date in dataByDate) {
newData.push(dataByDate[date]);
}
console.log(newData);
We want to reduce the data by unique dates. This can be done with:
An object as a dictionary,
Set or Map, or
Some other custom implementation.
Prefer to use Array.reduce() when reducing an array. This is standardized and more expressive than a custom implementation.
Using a map-like structure as the accumulator allows reduction of the dates by uniqueness and the data itself, simultaneously.
Note: Properties of objects are converted to Strings (except for Symbols). So if you want to use different "keys" that are equal after conversion (e.g. 0 and "0"), you cannot use objects; use Map instead.
(All our dates are Strings already, so this warning does not apply here.)
When using an object we can use the nullish coalescing assignment ??=: This allows us to assign an initial "empty" entry ({ date: dataEntry.date }) when encountering a new unique date.
Further, that assignment evaluates to the dictionary's entry; the entry that was either already present or just assigned.
Then we only need to assign the treatment and its count as a key-value pair to the entry.
const data = [
{ "date": "12/22", "treatment": "nausea", "count": 2 },
{ "date": "12/23", "treatment": "cold", "count": 3 },
{ "date": "12/22", "treatment": "cold", "count": 2 }
];
const newData = reduceByDate(data);
console.log(newData);
function reduceByDate(data) {
const dataByDate = data.reduce((dict, dataEntry) => {
const dictEntry = dict[dataEntry.date] // Get existing or ...
??= { date: dataEntry.date }; // ... just initialized entry.
dictEntry[dataEntry.treatment] = dataEntry.count;
return dict;
}, {});
// Transform dictionary to array of reduced entries
return Object.values(dataByDate);
}
You can make use of reduce() and Object.assign().
First we use reduce to combine objects with the same date into one object and then use assign to merge the values:
const data = [{
"date": "12/22",
"treatment": "nausea",
"count": 2
},
{
"date": "12/23",
"treatment": "cold",
"count": 3
},
{
"date": "12/22",
"treatment": "cold",
"count": 2
}
];
const newData = data.reduce((acc, curr) => {
const dateIndex = acc.findIndex(item => item.date === curr.date);
if (dateIndex === -1) {
acc.push({
date: curr.date,
[curr.treatment]: curr.count
});
} else {
acc[dateIndex] = Object.assign({}, acc[dateIndex], {
[curr.treatment]: curr.count
});
}
return acc;
}, []);
console.log(newData)

How to do a join from 2 JSON by a common ID field in Javascript

I have two JSON files: JSON A has some company properties and the company_id, while JSON B has company names and company ids.
JSON A example:
[
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
]
JSONB example:
[
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
]
Which is the most efficient way to do a join by the company_id values? I would like to have the JSON C (merged result) with the company names correctly added, like this:
[
{
"order_name": "Foo",
"company_id": "123456",
"company_name": "John Doe Inc."
},
{
"order_name": "Bar",
"company_id": "112233",
"company_name": "ACME company"
}
]
Is looping and filter for each the only solution? Is there a more efficient way to do this from a performance point of view?
More info:
JSON is not sorted by company_id.
Array A could have more than one object with the same company_id
I'm using Javascript (in a Vue.js app), I don't need to support old browsers
In common modern JavaScript, you can do this as you mentioned with higher-order functions like map, filter, and so on:
const arrayA = [
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
]
const arrayB = [
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
]
const mergeAB = arrayA.map( companyA => {
const matched = arrayB.find(companyB => companyB.company_id === companyA.company_id)
if(matched) {
return {...companyA, ...matched}
} else {
// return companyA element or customize it with your case
}
}
)
console.log(mergeAB)
Note 1: Array.find() method complexity is O(n) and Array.map() method complexity is O(n)
Note 2: efficiency is an important thing but not in all situations. sometimes you need to do these types of iteration one time or for a small array size, so no need to worry about the performance.
Note 3: you could compare the answer and find out your best solution since we don't know about your whole code and application.
I hope this will work for you. Let me know if you have any questions.
const arrayOne = [
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
];
const arrayTwo = [
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
];
const [source, target] = arrayOne.length > arrayTwo.length
? [arrayOne, arrayTwo]
: [arrayTwo, arrayOne];
const merged = source.map(object =>
{
// Assuming that in the 2nd array, the match is only found 1 time and it EXISTS.
const matched = target.find(element => element.company_id === object.company_id);
// Merge both objects together
return {
...object,
...matched
};
});
console.log(merged);
By having JSONs:
const jsonA = [
{
"order_name": "Foo",
"company_id": "112233"
},
{
"order_name": "Bar",
"company_id": "123456"
}
];
const jsonB = [
{
"company_id":"112233",
"name":"ACME company",
},
{
"company_id":"123456",
"name":"John Doe Inc.",
}
];
you can merge maps into 3rd map with something like this:
const transform = (data, current={}) =>
data.reduce((prev, company) => {
if(!prev[company['company_id']]) prev[company['company_id']] = {};
prev[company['company_id']] = {...prev[company['company_id']], ...company}
return prev;
}, current);
let jsonMap = transform(jsonA, {});
jsonMap = transform(jsonB, jsonMap);
let jsonC = Object.keys(jsonMap).map(companyId => jsonMap[companyId] );
console.log(jsonC);

How to push JSON data in two dimensional array?

I am trying to get a structure like
var tableData= [
['Hfbj'],
['Hygh'],
[6],
['mayur'],
[2563458952]
]
Here is my JSON data:
data:{
"address"': "Hfbj"
"id": 6
"landmark": "Hygh"
"name": "mayur"
"phone": 2563458952
"title": "aaa"
}
I am using react-native-table-component in which that type of structure needed. For that, I am doing the following but it showing data.map is not function and undefined.
let newdata = this.state.tableData[0].push(
[responseJson.data.title],
[responseJson.data.title]
);
this.setState({
tableData: newdata
});
How can I achieve it?
You could make use of Object.values and Array.map:
var reponseJson = {
data: {
"address": "Hfbj",
"id": 6,
"landmark": "Hygh",
"name": "mayur",
"phone": 2563458952,
"title": "aaa"
}
};
var newData = Object.values(reponseJson.data)
.map(item => [item]);
console.log(newData);
Note that I used the responseJson name to match your question, but as #Andreas pointed out, this is an object, not JSON.
If you need only certain columns (as requested in the comments below), use Object.keys and Array.filter on them before rebuilding the array:
var reponseJson = {
data: {
"address": "Hfbj",
"id": 6,
"landmark": "Hygh",
"name": "mayur",
"phone": 2563458952,
"title": "aaa"
}
};
var keysToKeep = ['address', 'landmark', 'title'];
var newData = Object.keys(reponseJson.data)
.filter(key => keysToKeep.includes(key))
.map(key => [reponseJson.data[key]]);
console.log(newData);
.map Is for arrays, whereas your responseJson.data is an Object. To get turn that Object into an array of its values you can do Object.values(responseJson.data)

convert nested object to one level up object in javascript

given json : -
{
"_id": "5c1c4b2defb4ab11f801f30d",
"name": "Ray15",
"email": "ray15#gmail.com",
"deviceToken": "dgtssgeegwes",
"deviceType": "IOS",
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1YzFjNGIyZGVmYjRhYjExZjgwMWYzMGQiLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTQ1MzU4MTI2fQ.YdK0MjOm7Lff22uTFITQdic0gKdMZRpsmRee-yejDpQ"
}
]
}
desired json: -
{
"_id": "5c1c4b2defb4ab11f801f30d",
"name": "Ray15",
"email": "ray15#gmail.com",
"deviceToken": "dgtssgeegwes",
"deviceType": "IOS",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1YzFjNGIyZGVmYjRhYjExZjgwMWYzMGQiLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTQ1MzU4MTI2fQ.YdK0MjOm7Lff22uTFITQdic0gKdMZRpsmRee-yejDpQ"
}
I want to convert JSON with the help of lodash library of npm in javascript or suggest any other library,
it might be a silly question, Please explain it properly, I am a newbie in javascript and try to learn node.js. comment me if you need more explanation.
Thanks for help
You don't really need a library, you can just assign the property and delete the other.
However tokens is an array, which suggest there might be more than one. This will only take the first one (obj.tokens[0].token). Since objects can't have duplicate keys, you will only be able to have one token with your desired format (if that matters).
let obj = {
"_id": "5c1c4b2defb4ab11f801f30d",
"name": "Ray15",
"email": "ray15#gmail.com",
"deviceToken": "dgtssgeegwes",
"deviceType": "IOS",
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1YzFjNGIyZGVmYjRhYjExZjgwMWYzMGQiLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTQ1MzU4MTI2fQ.YdK0MjOm7Lff22uTFITQdic0gKdMZRpsmRee-yejDpQ"
}
]
}
obj.token = obj.tokens[0].token
delete obj.tokens
console.log(obj)
There are a number of ways to solve this problem and no one "right" way. However, you may want to consider creating a new object, rather than mutating the original object. Objects are always passed by reference in JavaScript and it's easy to accidentally modify an object inside a function, not realizing that you just changed that object everywhere else it's referenced as well.
Since you mentioned it, here is a way to solve this with Lodash.
const obj = {
"_id": "5c1c4b2defb4ab11f801f30d",
"name": "Ray15",
"email": "ray15#gmail.com",
"deviceToken": "dgtssgeegwes",
"deviceType": "IOS",
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1YzFjNGIyZGVmYjRhYjExZjgwMWYzMGQiLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTQ1MzU4MTI2fQ.YdK0MjOm7Lff22uTFITQdic0gKdMZRpsmRee-yejDpQ"
}
]
};
// create a new object without the tokens property
const newObj = _.omit(obj, 'tokens');
// get the first token object from the tokens array
const tokenObj = _.head(obj.tokens);
// get the token string from the token object, defaulting to empty string if not found
newObj.token = _.get(tokenObj, 'token', '');
console.log(newObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Lodash is a great library and used by many projects. It can be especially helpful for new developers. For example, _.head(arr) will return undefined if arr is undefined. However, arr[0] would crash in the same scenario.
Here's one way to solve it without a library.
const obj = {
"_id": "5c1c4b2defb4ab11f801f30d",
"name": "Ray15",
"email": "ray15#gmail.com",
"deviceToken": "dgtssgeegwes",
"deviceType": "IOS",
"tokens": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1YzFjNGIyZGVmYjRhYjExZjgwMWYzMGQiLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTQ1MzU4MTI2fQ.YdK0MjOm7Lff22uTFITQdic0gKdMZRpsmRee-yejDpQ"
}
]
};
// create a copy of the original object.
// note that Object.assign will make a shallow copy of our object,
// so newObj.tokens will be a pointer to obj.tokens.
// in this instance, we don't care, as we are going to remove newObj.tokens anyway.
const newObj = Object.assign({}, obj);
// throw away the tokens property.
// OK to mutate newObj as we know it is not used anywhere else.
delete newObj.tokens;
// get the first token object from the tokens array.
// the (expectedArray || []) pattern ensures we have an array if obj.tokens is null or undefined.
const tokenObj = (obj.tokens || [])[0];
// get the token string from the token object.
// again, using the (expectedObject || {}) pattern in case tokenObj is null or undefined.
const token = (tokenObj || {}).token;
// create a new property called "token" on our newObj object.
// set it to our token value or an empty string if token is null or undefined.
newObj.token = token || '';
// of course, if you know the tokens array will always have a valid token object,
// you can simply use newObj.token = obj.tokens[0].token.
console.log(newObj);
Using destructuring assignment with "empty" representations of your types works nicely. transform produces a reliable output when tokens contains zero, one, or many { token: ... } values.
const emptyUser =
{ _id: 0, name: "", tokens: [] }
const emptyToken =
{ token: "" }
const toSingleTokenUser =
({ tokens: [ { token } = emptyToken ], ...u } = emptyUser) =>
({ ...u, token })
console .log
( toSingleTokenUser ({ _id: 1, name: "a", tokens: [ { token: "t" } ] })
// { _id: 1, name: "a", token: "t" }
, toSingleTokenUser ({ _id: 1, name: "a", tokens: [] })
// { _id: 1, name: "a", token: "" }
, toSingleTokenUser ({ _id: 1, name: "a", tokens: [ { token: "t1" }, { token: "t2" } ] })
// { _id: 1, name: "a", token: "t1" }
, toSingleTokenUser ({ foo: "bar", tokens: [ { token: "t" } ] })
// { foo: "bar", token: "t" }
)

MongoDB update nested object dependent on json input

I have json data that is structured in the following form:
[
{"size":100,"year":2015,"geography":"London","age":"21","gender":"Female"},
{"size":80,"year":2015,"geography":"Cardiff","age":"38","gender":"Male"},
{"size":80,"year":2013,"geography":"Edinburgh","age":"36","gender":"All"}
]
And I am trying to add it to a database collection with the following schema:
const Schema = new Schema({
geography: String,
size: {
2013: {
male: {
age: {
}
}
female: {
age: {
}
}
all: {
age: {
}
}
}
}
});
I'm currently trying to set up my update to work in the following way:
query = { geography : geography};
update = { geography : geography, $set: { "year.gender.age" : size}
Schema.updateOne( query, update, { upsert: true };
The $set here obviously does not work as the update does not know to pull the values from the json data. I have tried making year, gender and age variables and then refactoring the set (see below) but this does not work either.
$set: { `${year}.${gender{.$age}` }
So the question is how can I use the values in my JSON data to determine which embedded field to update?
The better option here would be a different schema which is not dynamic, as suggested in this answer.
Nonetheless, if you are to go with the current schema, you can map the values in the JSON as follows:
For example, if you have an object like
const obj = {
"size": 100,
"year": 2015,
"geography": "London",
"age": "21",
"gender": "Female"
}
to transform it to an update object of the form
{
"geography": "London",
"2015.female.21": 100
}
requires the following map:
const obj = {
"size": 100,
"year": 2015,
"geography": "London",
"age": "21",
"gender": "Female"
}
const doc = Object.keys(obj).reduce((acc, curr) => {
if (curr === "geography") {
acc["geography"] = obj[curr];
return acc;
};
switch(curr) {
case "year":
case "gender":
case "age":
acc[`${obj['year']}.${obj['gender'].toLowerCase()}.${obj['age']}`] = obj['size']
}
return acc;
}, {});
console.log(JSON.stringify(doc, null, 4));
which you can then use in your update operation as
Model.updateOne( query, { '$set': doc }, { upsert: true }, callback );
in order to create object like this-
{"size":100,"year":2015,"geography":"London","age":"21","gender":"Female"}
I think you need to set your schema to:
const Schema = new Schema({
geography: String,
size: Number,
year,: String,
gender: String,
age: String
});
Then to update use something like:
update = { {$and: [{year:"2015"}, {age:"33"} /*and more*/]}, $set: { size : size}

Categories