convert nested object to one level up object in javascript - 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" }
)

Related

Conditionally combine arrays of objects based on Ids

How can I add the status property from object2 into object1 based on newEmployeeId matching employeeId only if the dependentId is NULL.
For example: An array where Ben, Jim and Dan have statuses of Complete, Updating and Finished, respectively. Lauren should not have a status.
var object1 = [
{ name: 'Ben', employeeId: 1, dependentId: null },
{ name: 'Lauren', employeeId: 1, dependentId: 5},
{ name: 'Jim', employeeId: 2, dependentId: null },
{ name: 'Dan', employeeId: 3, dependentId: null}
];
var object2 = [
{ status: 'Complete', newEmployeeId: 1 },
{ status: 'Updating', newEmployeeId: 2 },
{ status: 'Finished', newEmployeeId: 3 }
];
There are a number of ways to approach this. Here's one:
object1.forEach(o1 => {
if (o1.dependentId !== null) return;
const o2 = object2.find(o2 => o2.newEmployeeId === o1.employeeId);
if (!o2) return;
o1.status = o2.status;
})
The idea is that we loop over each entry of object1 via the forEach() array method. For each such entry o1, we first check to make sure that its dependentId is null (because you only want to operate on such entries) and give up if it isn't.
Then we search the object2 array for a matching entry o2 via the find() array method. If no such entry is found, o2 will be undefined and we we give up. Otherwise we set o1's status property to match that of o2.
For the example code you gave, this produces the following final state for object1:
console.log(object1);
/* [{
"name": "Ben",
"employeeId": 1,
"dependentId": null,
"status": "Complete"
}, {
"name": "Lauren",
"employeeId": 1,
"dependentId": 5
}, {
"name": "Jim",
"employeeId": 2,
"dependentId": null,
"status": "Updating"
}, {
"name": "Dan",
"employeeId": 3,
"dependentId": null,
"status": "Finished"
}] */
Note that, depending on your use cases, you might want to change the implementation. For example, if your arrays have many entries, you might want to index object2 by newEmployeeId ahead of time instead of finding its elements over and over again. But that's outside the scope of the question as asked.
Playground link to code
Try this:
object1.forEach(item => {
if (!item.dependentId) {
result = object2.find(item2 => item2.newEmployeeId === item.employeeId)
item.status = result.status
}
})

flattening nested objects typescript

I am looking to flatten a nested object in my controller (new to Loopback and Typescript)
Here is my model :
export class SampleModel {
id: number;
code: number;
guide?: string;
gradeData?: string;
}
Here is an example object :
{
"id": 1,
"code": 12345,
"guide": "Guide for 2021",
"gradeData": {
"en": "Eng grade",
"de": "Ger grade"
}
}
Here is my controller:
// returns an array of SampleModel objects
#get('/guides')
async find(
#param.query.string('lang') lang: string,
#param.filter(SampleModel) filter?: Filter<SampleModel>
): Promise<SampleModel[]> {
return this.sampleModelRepository.find(filter); //this returns Promise<SampleModel[]>
}
I want to tweak this response a little based on lang. For ex: if lang = en I want the response to look like
[
{
"id": 1,
"code": 12345,
"guide": "Guide for 2021",
"gradeData": "Eng grade"
}
]
Something like this?
Ofcource you need to make the langcode dynamic
[{
"id": 1,
"code": 12345,
"guide": "Guide for 2021",
"gradeData": {
"en": "Eng grade",
"de": "Ger grade"
}
}].map(e=>{
e.gradeData = e.gradeData["en"];
return e;
})
Returned object:
[
{
"id": 1,
"code": 12345,
"guide": "Guide for 2021",
"gradeData": "Eng grade"
}
]
Thanks to #Firewizz I was able to do this. Here is my updated controller :
// returns an array of SampleModel objects
#get("/guides")
async find(
#param.query.string("lang") lang: string,
#param.filter(SampleModel) filter?: Filter<SampleModel>
): Promise<SampleModel[]> {
const res = this.sampleModelRepository.find(filter); //this returns Promise<SampleModel[]>
if (lang != null) {
(await res).map((e) => {
if (e.gradeData != null && e.gradeData.hasOwnProperty(lang)) {
e.gradeData = new Map(Object.entries(e.gradeData)).get(locale);
// not sure why this is required but when I tried
// `e.gradeData = e.gradeData[locale];`
// I get compilation error " Element implicity has an 'any' type because index expression is not of type 'number' " maybe because gradeData is defined as a String but when I do
// console.log(typeof e.gradeData)
// I get object
// I also tried
// `e.gradeData = JSON.parse(e.gradeData)[locale];`
// I get " SyntaxError: Unexpected token o in JSON at position 1 " and that could be because it's already an object
// I then tried
// `e.gradeData = JSON.parse(JSON.stringify(e.gradeData))[locale];`
// this also works but I think converting this to a map as a workaround is better
}
return e;
});
}
return res;
}

IndexOf over a nested array response graph returning just []

I am trying to filter some articles from a graphql response, by articleTag. Se my structure below:
{
"id": "41744081",
"articleTitle": "text",
"articleContent": "text",
"categoryName": { "categoryName": "Company", "id": "38775744" },
"articleTags": [
{ "articleTag": "event", "id": "37056861" },
{ "articleTag": "car", "id": "37052481" },
]
},
{
"id": "41754317",
"articleTitle": "text",
"articleContent": "text",
"categoryName": { "categoryName": "Sales and Martketing", "id": "38775763" },
"articleTags": [{ "articleTag": "technology", "id": "37056753" }]
},
...
But when applying my function:
notificationFiltered () {
var articleResponse = this.response.allArticles;
var routeParam = this.$route.params.tagName; //contains the id of the tag
const filteredTag = articleResponse.filter((item) => {
return (item.articleTags.indexOf(routeParam) >= 0);
});
console.log(filteredTag);
},
When I'm "console.log" the result I get only a "[]". Not sure if is related with the way of query is being render, in the API I get the same formation but with this slightly difference
{
"data": {
"allArticles": [... the specify structure above]
}
}
while printing that with vue {{response.allArticles}} I just get the first structure, I think it shouldn't matter?
Thanks in advance for the advice
You won't be able to use indexOf for array of objects to find a matching object - only strict equality is needed, and that's hard to get in the reference land. Consider this:
const objs = [
{ foo: 'bar' },
{ foo: 'baz' },
{ foo: 'foo' } // whatever
];
const needle = { foo: 'baz' };
objs.indexOf(needle);
// -1
What? Yes, there's an object looking exactly like needle in that array - but it's a different object:
objs[1] === needle; // false
That's why indexOf just goes past that one - and gives out -1, a "not found" result.
What you should be able to use in this case is findIndex. Still you need to build the predicate to have a match. For example:
const objs = [
{ foo: 'bar' },
{ foo: 'baz' },
{ foo: 'foo' }
];
const needle = { foo: 'baz' };
objs.findIndex(el => JSON.stringify(el) === JSON.stringify(needle));
// 1
In this example, comparing results of JSON.stringify in the predicate function is a poor man's _.isEqual - just to illustrate the concept. What you should consider actually using in your code is either _.isEqual itself, or similar function available in toolkit of your choice.
Alternatively, you can just check for specific fields' values:
objs.findIndex(el => el.foo === needle.foo); // still 1
This will apparently find objects even if their other properties do not match though.

convert return object to json but only certain value pairs

Yes I know there are heaps of posts about converting objects to json but my question is more specific..
Say Im calling some data from an api and the response is an object that looks like this
{
date: ...,
value: ...,
useless-info: ...,
useless-info: ...
}
now I know I can do this JSON.stringify(returnedobject);
so I get the newly formed json..
{
"date": ...,
"value": ...,
"useless-info": ...,
"useless-info": ...
}
now all I want in my newly formed json to be the "date" and "value" and remove the useless-info is this even possible?
any help would be appreciated!
Working Demo
var jsonObj = {
"date": "",
"value": "",
"useless-info": "",
"useless-info": ""
};
delete jsonObj["useless-info"];
var jsonString = JSON.stringify(jsonObj);
console.log(jsonString);
JSON.stringify() has a replacer param that can be used to limit output to a whitelisted array of keys you want to keep.
// Input.
const input = {
date: new Date(),
value: 8905934,
useless: 'useless',
extra: 'extra'
}
// Output.
const output = JSON.stringify(input, ['date', 'value'])
// Proof.
console.log(output)
const oldJson = {
"date": ...,
"value": ...,
"useless-info": ...,
"useless-info": ...
}
const newJson = {
"date" : oldJson.date,
"value": oldJson.value
}
You can either create a new object with the data you want, or delete the fields you don't need:
const someReturn = {
date: ...,
value: ...,
badstuff: ...
}
const goodObj = {
date: someReturn.date,
value: someReturn.value
}
Or to delete fields you can just call delete someReturn.badstuff

Node.js/express parsing data and still getting back [object Object] in response and some data is missing

As stated above I am using NODE/Express backend and trying to parse out some data before I send it to the front-end.
I have an array of objects(items) and want to parse out a certain field in each item, particularly the description and the geolocation. It seems like it works just fine, but I have a couple of issues I am running into.
First I will show you one of the items to show you what it was before:
{
"_id": {
"$oid": "59925302e12872a81b28099e"
},
"offer": "19f5d9ef37b30311",
"txid": "389eb024f8869d787954c74d1653b8358e581cb4c81358361ffac662875bceec",
"expires_in": 13770133,
"expires_on": 1516531809,
"expired": false,
"height": "46411",
"category": "for sale",
"title": "Jack Garratt Signed Deluxe CD",
"quantity": "1",
"currency": "USD",
"sysprice": 415240000000,
"price": "35.00",
"ismine": false,
"commission": "0",
"offerlink": "false",
"private": "No",
"paymentoptions": 2,
"paymentoptions_display": "BTC",
"alias_peg": "sysrates.peg",
"description": "{\"description\":\"Signed, near mint double CD Only played a very few times\",\"media\":{\"defaultIdx\": 0,\"mediaVault\": [{\"mediaType\":\"img\",\"mediaURL\":\"http://i.imgur.com/bB7QjDR.jpg\"}]}}",
"alias": "Signed, near mint double CD Only played a very few times http://i.imgur.com/bB7QjDR.jpg",
"address": "1GR389Tki2LS3jScFUhrVxVnXdPdED5839",
"alias_rating_display": "0.0/5 (0 Votes)",
"offers_sold": 0,
"geolocation": "{\"coords\":{\"lat\":36.8518706,\"lng\":-123.5029326}}",
"alias_rating_count": 0,
"alias_rating": 0,
"safetylevel": 0,
"safesearch": "Yes",
"offerlink_seller": "",
"offerlink_guid": "",
"time": {
"$date": "1970-01-18T04:29:58.326Z"
},
"cert": "",
"__v": 0
}
Next I will show you how I parse the data I want. I particularly want a description geolocation and a new field media.
let newResults = [];
let updatedItem = {};
results.map((item, i) => {
const newDescription = JSON.parse(item.description);
const newGeolocation = JSON.parse(item.geolocation);
console.log(newGeolocation)
updatedItem = item;
updatedItem.geolocation = newGeolocation;
updatedItem.media = newDescription.media;
updatedItem.description = newDescription.description;
newResults.push(updatedItem);
return newResults;
});
console.log(newResults[24]);
Here is the result of console.log(newResults[24]):
{ _id: 59925302e12872a81b2809a3,
offer: '17fff08820c6da06',
txid: 'f27ec82c4cd694ecfdf061ebff7709a6154e39767595f7da08e4b2a40503c816',
expires_in: 26828208,
expires_on: 1529589884,
expired: false,
height: '276435',
category: 'for sale',
title: 'Marijuana Flavoured Vape E-Liquid 10ml 6mg',
quantity: '19',
currency: 'GBP',
sysprice: 1912000000,
price: '2.00',
ismine: false,
commission: '0',
offerlink: 'false',
private: 'No',
paymentoptions: 1,
paymentoptions_display: 'SYS',
alias_peg: 'sysrates.peg',
description: 'Marijuana Flavoured E-Liquid 10ml 6mg',
alias: 'Marijuana Flavoured E-Liquid 10ml 6mg',
address: '1DNeg3CrRFx6PuLcCry26p9y2XiTzTTFqw',
alias_rating_display: '0.0/5 (0 Votes)',
__v: 0,
offers_sold: 0,
geolocation: '[object Object]',
alias_rating_count: 0,
alias_rating: 0,
safetylevel: 0,
safesearch: 'Yes',
offerlink_seller: '',
offerlink_guid: '',
time: Sun Jan 18 1970 02:32:37 GMT-0600 (Central Standard Time),
cert: '' }
As you can see the data seems to have been parsed for the description, but the geolocation is giving me a weird [object Object] even though if I console log it inside the map it gives me each of the parsed data and they look fine.
Something else that I want to note here is that the media field is not even there. But if I were to console.log(newResults[24].media) it shows me the parsed data. But if I do try to access it on the front end like item.media I get undefined which is expected since it does not show up.
You are misusing map(). map() will return an object out of it. Try using something like this:
let newResults = results.map((item, i) => {
const {
media,
description
} = JSON.parse(item.description);
const geolocation = JSON.parse(item.geolocation);
return {
geolocation,
media,
description
};
});
console.log(newResults[24]);
If you want to tack these properties onto the item (without mutating the original item) you can use Object.assign() which will dump all the properties of the objects from the second argument onward into whatever the first argument is with priority given to later arguments.
In other words, we are going to return the value returned by the call to Object.assign({}, item, {...new props...}) and that call is going to create a new Object {} then dump all the properties from item into that new Object and then it is going to dump all the properties from {...new props...} on that first {} argument and will overwrite properties if they are already there.
let newResults = results.map((item, i) => {
const {
media,
description
} = JSON.parse(item.description);
const geolocation = JSON.parse(item.geolocation);
return Object.assign({}, item, {
geolocation,
media,
description
});
});
Additionally, when printing nested objects in node.js with console.log(), node will truncate the output rather than printing the whole arrary. Consider this code:
let tmp = [
{
foo: "Bar",
fizz: [
{
buzz: "hello"
}
]
}
];
console.log(tmp);
Will print:
[ { foo: 'Bar', fizz: [ [Object] ] } ]
The object is still fine, and if you try to dereference the nested object itself with console.log(tmp[0].fizz[0].buzz); You will get the output: hello.
Try
results.map((item, i) => {
const newDescription = JSON.parse(item.description);
const newGeolocation = JSON.parse(item.geolocation);
console.log(newDescription.media)
updatedItem = item;
updatedItem.geolocation = item.geolocation; //just assign geolocation
updatedItem.media = JSON.stringify(newDescription.media); //stringify the media object
updatedItem.description = newDescription.description;
newResults.push(updatedItem);
return newResults;
});

Categories