Async getter function in JSON - javascript

I have a project in Node and Typescript in which I am trying to connect to the mongoose Schema from the property of an object, only if the Schema has the same name as the value of the name property
These are the data:
Schema: const Company = require("../../schemas/companies")
MENU_PROPERTIES:
const MENU_PROPERTIES = [
{
"name":"Company",
"icon":"local_shipping"
},
{
"name":"Client",
"icon":"people"
},
{
"name":"Employee",
"icon":"account_circle"
}
]
MenuActions:
interface MenuActions {
name: string,
icon: string,
total(): Promise<number>
}
This is the first function I've tried:
let menuData: MenuActions[] = [];
for (let menu of MENU_PROPERTIES) {
menuData.push({
name: menu.name,
icon: menu.icon,
get total() {
return SCHEMA_LIST.includes(this.name) ? eval(this.name).find().count() : 0
}
})
}
Result: ERROR
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Topology'
| property 's' -> object with constructor 'Object'
| property 'sessionPool' -> object with constructor 'ServerSessionPool'
--- property 'topology' closes the circle
at JSON.stringify (<anonymous>)
at stringify (C:\Users\quality\Desktop\proyectoFinal\project-server\node_modules\express\lib\response.js:1123:12)
at ServerResponse.json (C:\Users\quality\Desktop\proyectoFinal\project-server\node_modules\express\lib\response.js:260:14)
at returnHandler (C:\Users\quality\Desktop\proyectoFinal\project-server\build\src\routes.js:148:48)
at C:\Users\quality\Desktop\proyectoFinal\project-server\build\src\routes.js:132:13
This is the second function:
let menuData: MenuActions[] = [];
for (let menu of MENU_PROPERTIES) {
menuData.push({
name: menu.name,
icon: menu.icon,
get total() {
return (async () => {
return SCHEMA_LIST.includes(this.name) ? await eval(this.name).find().count() : 0
});
}
})
}
Result: This function does not return anything, the total property does not appear in the object
[
{
"name":"Company",
"icon":"local_shipping"
},
{
"name":"Client",
"icon":"people"
},
{
"name":"Employee",
"icon":"account_circle"
}
]

Related

Why am I unable to send a variable as URL in a Planner Task reference?

I am trying to set references for a task via the Planner Graph API, but am unable to set the URL through a variable.
I have written a separate method for encoding the url, and it returns the correct value, but still the references aren't set for the Planner task.
const updateAttachedFiles = (
links: string[],
taskId: string,
etag: string
) => {
var encodedLink: string;
links.forEach(async (link) => {
encodedLink = escapeHtml(link)
await graph.planner.tasks.getById(taskId).details.update(
{
references: {
encodedLink : {
"#odata.type": "microsoft.graph.plannerExternalReference",
"previewPriority": " !",
type: "Other",
},
},
},
etag);
}
)
};
const escapeHtml = (unsafe) => {
let temp = unsafe.replaceAll("%", "%25")
unsafe = temp
.replaceAll(".", "%2E")
.replaceAll(":", "%3A")
.replaceAll("#", "%40")
.replaceAll("#", "%23");
return unsafe
}
But if I change encodedLink to the hardcoded URL (copied from the value set in the variable encodedLink), it works.
{
references: {
"https%3A//shmafe%2Esharepoint%2Ecom/sites/PlannerTest1/Delade dokument/nedladdning%2Ejpg" : {
"#odata.type": "microsoft.graph.plannerExternalReference",
"previewPriority": " !",
type: "Other",
},
},
}
I need to be able to set the link dynamically, so how can I do it without being able to use a variable? Am I doing something else wrong?
Microsft documenttion for Update plannertaskdetails
https://learn.microsoft.com/en-us/graph/api/plannertaskdetails-update?view=graph-rest-1.0&tabs=javascript
Microsft documenttion for plannerExternalReferences resource type
https://learn.microsoft.com/en-us/graph/api/resources/plannerexternalreferences?view=graph-rest-1.0
To use a variable as an object key, you need to use the bracket syntax
For example:
const myVariable = 'hello';
const demoObject = {
[myVariable]: 'world'
};
console.log(demoObject[myVariable]);
// same as
console.log(demoObject.hello);
This should fix it:
const updateAttachedFiles = (
links: string[],
taskId: string,
etag: string
) => {
var encodedLink: string;
links.forEach(async (link) => {
encodedLink = encodeURI(link)
await graph.planner.tasks.getById(taskId).details.update(
{
references: {
[encodedLink] : {
"#odata.type": "microsoft.graph.plannerExternalReference",
"previewPriority": " !",
type: "Other",
},
},
},
etag);
}
)
};

Create new object from array

I'm trying to create new object with different properties name from Array.
Array is:
profiles: Array(1)
0:
column:
name: "profileName"
title: "Profile name"
status: "Active"
I want to create new function that return object with two properties:
id: 'profileName',
profileStatus: 'Active'
The function that I have create is returning only one property as undefined undefined=undefined.
function getProfile(profiles) {
if (!profiles.length) return undefined;
return profiles.reduce((obj, profile) => {
console.log('profiles', profile);
return ({
...obj,
id: profile.column.name,
profileStatus: profile.status,
});
}, {});
}
The function getProfile is taking as input array 'profiles' from outside,
I've just tested here and this seems to be working actually
const getProfile1 = (p) => p.reduce((obj, profile) =>({
...obj,
id: profile.column.name,
profileStatus: profile.status,
}), {});
You can use map as an alternative.
var profiles = [{"column":{"name": "profileName3","title": "3Profile name"},"status": "Active"},{"column":{"name": "profileName","title": "Profile name"},"status": "Active"}];
function getProfile(profiles) {
if (!profiles.length) return undefined;
return profiles.map(function(profile,v){
return {id:profile.column.name,profileStatus: profile.status};
});
}
console.log(getProfile(profiles));
Whenever I use reduce in this way, I usually index the final object by some sort of an id. As noted in another answer, you could use map in this situation as well. If you really want your final data structure to be an object, however, you could do something like this:
/**
* returns object indexed by profile id
*/
const formatProfiles = (profiles) => {
return profiles.reduce((obj, profile) => {
return {
...obj,
[profile.id]: {
id: profile.column.name,
profileStatus: profile.status,
}
};
}, {});
};
const profiles = [
{
id: 0,
status: 'active',
column: {
name: "profile_name_1",
title: "profile_title_1",
},
},
{
id: 1,
status: 'inactive',
column: {
name: "profile_name_2",
title: "profile_title_2",
}
}
];
const result = formatProfiles(profiles);
/**
* Result would look like this:
*/
// {
// '0': { id: 'profile_name_1', profileStatus: 'active' },
// '1': { id: 'profile_name_2', profileStatus: 'inactive' }
// }

JavaScript - Properly Extract Deep Object Properties and Construct New Object

Suppose the following array of objects is returned from an API:
const data = [
{ // first item
meta: {
stems: [
"serpentine",
"serpentinely"
]
},
hwi: {
hw: "sep*pen*tine",
prs: [
{
mw: "ˈsər-pən-ˌtēn",
sound: {
audio: "serpen02"
}
},
]
},
shortdef: [
"of or resembling a serpent (as in form or movement)",
"subtly wily or tempting",
"winding or turning one way and another"
]
},
{ // second item
meta: {
stems: [
"moribund",
"moribundities",
"moribundity"
]
},
hwi: {
hw: "mor*i*bund",
},
fl: "adjective"
}
]
I want to create a function that will generate a new array of objects. The objects in this new array will consist of data from the old objects, just rearranged. This is how I expect a new array to look, for example:
[
{
word: 'serpentine',
definitions: [
'of or resembling a serpent (as in form or movement)',
'subtly wily or tempting',
'winding or turning one way and another'
]
},
{
word: 'moribund',
definitions: [
'being in the state of dying : approaching death',
'being in a state of inactivity or obsolescence'
],
partOfSpeech: 'adjective'
}
]
I do this with the following function:
const buildNewData = arr => {
const newData = []
arr.forEach(item => {
newData.push({
...item.meta.stems[0] && { word: item.meta.stems[0]},
...item.shortdef && { definitions: item.shortdef },
...item.fl && { partOfSpeech: item.fl },
...item.hwi.prs[0].mw && { pronunciation: item.hwi.prs[0].mw}
})
})
return newData
}
buildNewData(data)
You may be curious as to why I use ...item.meta.stems[0] && { word: item.meta.stems[0]} in the creation of the new objects. This is to check if the property exists in the original object. If it doesn't exist, the expression will evaluate to false and therefore not be added to the new object. The first object in the original array does not have the fl property, so it evaluates to false when the new object is being constructed.
But this doesn't work when looking up a property that is an array. The code above fails with the error: TypeError: Cannot read property '0' of undefined. That's because the second item does not have a prs array under the hwi property, so the lookup fails.
Since I cannot control what data is returned from the API, how do I write a function that successfully creates a new array of objects in the format I've specified, without causing an error? I already have a solution to not add particular properties if they do not exist, but how do I take into account arrays?
More generally, I'm curious if there is a standardized way of extracting data from objects programmatically that prevents errors like this from occurring. Is there a better way to do this?
You need an additional guard so:
...item.hwi.prs[0].mw && { pronunciation: item.hwi.prs[0].mw}
becomes
...(Array.isArray(item.hwi.prs) && item.hwi.prs[0].mw) && { pronunciation: item.hwi.prs[0].mw}
which can be shortened to:
...(item.hwi.prs && item.hwi.prs[0].mw) && { pronunciation: item.hwi.prs[0].mw}
if you are confident that if item.hwi.prs exists its value will be an array that has a 0 value that can be spread.
const data = [
{ // first item
meta: {
stems: [
"serpentine",
"serpentinely"
]
},
hwi: {
hw: "sep*pen*tine",
prs: [
{
mw: "ˈsər-pən-ˌtēn",
sound: {
audio: "serpen02"
}
},
]
},
shortdef: [
"of or resembling a serpent (as in form or movement)",
"subtly wily or tempting",
"winding or turning one way and another"
]
},
{ // second item
meta: {
stems: [
"moribund",
"moribundities",
"moribundity"
]
},
hwi: {
hw: "mor*i*bund",
},
fl: "adjective"
}
];
const buildNewData = arr => {
const newData = []
arr.forEach(item => {
newData.push({
...item.meta.stems[0] && { word: item.meta.stems[0]},
...item.shortdef && { definitions: item.shortdef },
...item.fl && { partOfSpeech: item.fl },
...(Array.isArray(item.hwi.prs) && item.hwi.prs[0].mw) && { pronunciation: item.hwi.prs[0].mw}
})
})
return newData
}
let newData = buildNewData(data);
console.log(newData);
As you need to check existence of properties in an Object:
Use Optionnal chaining: https://javascript.info/optional-chaining
It returns a type undefined if the prop doesn't exist (but not string "undefined" ;) )
For desired order in new array, add numbers before the names of props.
let newData = [];
for (let i = 0; i < data.length; i++) {
newData[i] = {};
if (data[i]?.meta?.stems[i] != undefined)
newData[i].word = data[i].meta.stems[i];
if (data[i]?.shortdef != undefined) {
newData[i].definitions = data[i].shortdef.join(', ') + '.';
newData[i].definitions = newData[i].definitions.charAt(0).toUpperCase() + newData[i].definitions.substring(1); // Capitalize first letter
}
if (data[i]?.fl != undefined)
newData[i].partOfSpeech = data[i].fl;
}
console.log(...newData);

JSON cannot ready property of null

I am calling API that is returning JSON array I am iterating over this array and map each value to new object and then push it new array.
Issue is when value from API is null i get
Cannot read property 'name' of null
but in my code i am handling null but still getting this error....
let comArr = [];
JSONData.issues.forEach(element => {
comArr.push({
Resolution: (element.fields.resolution.name === null) ? "some default value" : element.fields.resolution.name,
});
});
it seems that element.fields.resolution is null in same cases. Add a second if check on your statement, like:
if (element.fields.resolution === null){
comArr.push({ Resolution: "some default value" });
}
else{
comArr.push(Resolution: (element.fields.resolution.name === null) ? "some default value" : element.fields.resolution.name);
}
The issue is that your resolution object is null in some cases, so you should make a check to see if the object exists first. A cleaner solution would be to use map, since you are always pushing an object, rather than using forEach:
const JSONData = {
issues: [
{
fields: {}
},
{
fields: {
resolution: {
name: 'foo'
}
}
},
{
fields: {}
},
{
fields: {
resolution: {
name: 'bar'
}
}
}
]
}
const output = JSONData.issues.map(({fields: {resolution}}) => ({
Resolution: resolution && resolution.name ? resolution.name : "some default value"
}))
console.log(output)

Typescript how to add new item to empty array of objects

I have component in vue 2 which is written in typescript:
data: {
userfilterresults: []
},
mounted() {
axios.get("/Tasks/GetTasks")
.then(response => {
this.userfilterresults = response.data;
});
},
methods: {
addtab() {
// this push bellow is the reason of the error:
this.userfilterresults.push({
id : '-1',
userid : '1',
name : 'newtab'
});
And I want to add new item to existed array userfilterresults but I've got error: Argument type {..} is not assignable to parameter of type never
How I can add new item to the array?
You need to declare a type for userfilterresults.
By default, for let x = [], type of x will be never[].
You need to specify it explicitly or mark it as any[]. e.g.
let x: any[] = []
let y: CustomType[] = []
I'm not familiar with the typings of Vue, but that is the underlying reason.
Try
data: {
userfilterresults: [] as any[]
}
and see if that helps.
userfilterresults:any= [] ;
addtab() {
var obj={
id : '-1',
userid : '1',
name : 'newtab'
} }
this.userfilterresults.push(obj);

Categories

Resources