I have a complicated data structure that I need to convert to JSON. The problem is that my field names and values are in an array.
For instance, I have the following (simplified from my code base):
let SampleData = [
{ Field: 'Key', Value: '7'},
{ Field: 'City', Value: 'Some City'},
{ Field: 'Description', Value: 'Some Description'}
];
Basically my data is an array where the first element is the database column name, and the second element is the data in the column. I am trying to get a JSON object that is:
{ Key: 7, City: 'Some City', Description: 'Some Description' }
My real code has the fields and data is structures within the object, so I cannot simply use an Object.create() or Object.assign() as far as I can get working.
I have tried looping through to build a simple string and then use the JSON.parse to break it apart, but this seems like a lot of overhead for something I would have thought would be simpler.
As you asked, here's how to do it:
Mapping the array to an object
Converting the object to JSON
let array = [{
Field: 'Key',
Value: '7'
},
{
Field: 'City',
Value: 'Some City'
},
{
Field: 'Description',
Value: 'Some Description'
}
];
// #1 Mapping the array to an object...
let obj = {};
array.forEach(item => obj[item.Field] = item.Value);
// #2 Converting the object to JSON...
let json = JSON.stringify(obj);
console.log(json);
Bonus (ES6 + reduce):
const obj = array.reduce((acc, { Field, Value }) => ({ ...acc, [Field]: Value }), {});
you can try the below approach . I have used spread operator(ES6) and Object.assign to create the object ,then converted it into json string.
let SampleData = [
{ Field: 'Key', Value: '7'},
{ Field: 'City', Value: 'Some City'},
{ Field: 'Description', Value: 'Some Description'}
];
let obj = Object.assign(...SampleData.map( x => Object.values(x)).map(y => ({[y[0]]: y[1]})));
console.log(obj);
//{ Key: "7", City: "Some City", Description: "Some Description" }
console.log(JSON.stringify(obj));
I had a similar requirement and here is how I achieved it.
var ranges: segmentRange[] = new Array(2);
ranges[0] = { minimumPercentage: 50, maximumPercentage: 60 };
ranges[1] = { minimumPercentage: 30, maximumPercentage: 40 };
const segmentRanges = { segmentRanges: ranges };
return JSON.stringify(segmentRanges);
Output:
{"segmentRanges":[{"minimumPercentage":50,"maximumPercentage":60},{"minimumPercentage":30,"maximumPercentage":40}]}
HTH,
Related
I have a React form that uses i18 next translations from JSON file in a dropdown. The objects are very simple: one key, one translation, like this (but the file is very long, there are dozens of keys).
JSON:
{
"country": "Country",
"big": "Big",
"dog": "Dog",
"integration": "Integration"
}
Code that uses data from JSON:
const createField = (parentName: string, item: any) => {
const field = {
type: `${item.type}`,
name: `${parentName ?? ''}${parentName?.length ? '.' : ''}${item.name}`,
label: t(`${item.label ?? item.name}`),
properties: {
placeholder: `${item.placeholder ?? ''}`
} as any,
};
if (item.type === 'select' ) {
field.properties = {
...field.properties,
options: [].concat(item.options?).sort((a,b) =>
t(`${a.value}`) > t(`${b.value}`) ? 1 : -1).map((option: any) => {
return {
label: t(`${option.label}`),
value: option.value
};
}),
};
};
};
I want the dropdown to be sorted according to the value alphabetically because in each language the order would be different. Everything I tried sorts the array from this JSON according to the key.
I tried concat(item.options?) for sorting but getting errors "Expression expected" and "property doesn't exist".
Also tried this solution, but the dropdowns turned from text into random numbers.
if (item.type === 'select' ) {
field.properties = {
...field.properties,
options: Object.entries(item.options)
.sort(([key1], [key2]) => t(key1).localeCompare(t(key2)))
.map(([label, value]) => ({
label: t(label),
value
}))
};
};
Issue
Assuming that item.options is the JSON data you are trying to sort and convert to a list of options, then the issue is that you've appended the entire options object instead of each individual option. The result is an array of length 1. Another issue is that your data is a dictionary of key-value pairs, not an array of objects with label and value properties.
Solution
You can use both Object.entries and Object.fromEntries to convert the object to an array of key-value pairs and back. For the sorting function you want to sort by the key, and since the keys are strings, use localeCompare for the string comparison.
const data = {
country: "Country",
big: "Big",
dog: "Dog",
integration: "Integration"
};
const sortedData = Object.fromEntries(
Object.entries(data).sort(([key1], [key2]) => key1.localeCompare(key2))
);
console.log(sortedData);
Since you really want an array of shape [{ label: '...', value: '...' }, ...] you can use an array.map to map the array of key-value pairs to an array of objects with the shape you need for mapping in the option values.
const data = {
country: "Country",
big: "Big",
dog: "Dog",
integration: "Integration"
};
const sortedData = Object.entries(data)
.sort(([key1], [key2]) => key1.localeCompare(key2))
.map(([label, value]) => ({
label,
value
}));
console.log(sortedData);
For the actual rendering of your options:
options: Object.entries(item.options)
.sort(([key1], [key2]) => t(key1).localeCompare(t(key2)))
.map(([label, value]) => ({
label: t(label),
value
}))
Since it's not very clear which of the key or value of the JSON data is your option label/value you may needs to tweak the above to fit. I can help here if needed.
Hope it helps:
sort object by key and result is an array of sorted keys
const obj={
"country": "Country",
"big": "Big",
"dog": "Dog",
"integration": "Integration"
}
const sortedKeys=Object.keys(obj).sort();
console.log(sortedKeys);
sort object by value and result is an array of sorted values
const obj={
"country": "Country",
"big": "Big",
"dog": "Dog",
"integration": "Integration"
}
const sortedValues=Object.values(obj).sort();
console.log(sortedValues)
sort object by value and result is an object
const obj={
"country": "Country",
"big": "Big",
"dog": "aDog",
"integration": "Integration"
}
//for case insensitive use this function
const sortedByValue=Object.values(obj).sort(function(a, b) {
return (a.toUpperCase() < b.toUpperCase()) ? -1 : (a.toUpperCase() > b.toUpperCase()) ? 1 : 0;
})
function getKeyByValue(value) {
return Object.keys(obj).find(key => obj[key] === value);
}
const sortedObj={};
sortedByValue.map(value=>{
const key=getKeyByValue(value)
sortedObj[key]=value;
})
console.log(sortedObj)
I have made Custom Fields in my Users in Google Suite.
Category: Foresatt
Among them:
Name: 'foresatt epost', type:email, number: multiple values
I would like to list these values using Google Script. I used this:
https://developers.google.com/admin-sdk/directory/v1/quickstart/apps-script
To write this code:
function listUsers() {
var optionalArgs = {
customer: 'my_customer',
maxResults: 10,
orderBy: 'email',
projection: 'custom',
customFieldMask:'Foresatt'
};
var response = AdminDirectory.Users.list(optionalArgs);
var users = response.users;
if (users && users.length > 0) {
Logger.log('Users:');
for (i = 0; i < users.length; i++) {
var user = users[i];
var foresatt = user.customSchemas;
Logger.log('%s (%s)', user.primaryEmail, user.name.fullName, foresatt);
}
} else {
Logger.log('No users found.');
}
}
That works, but I would like to get only the values. What I get now:
{Foresatt={
foresatt_mob=[{value=X#X#X#X#, type=work}, {type=work, value=X#X#X#X#}, {type=work, value=X#X#X#X#}],
foresatt_epost=[{value=xx#xx.no, type=work}, {type=work, value=xy#xx.no}, {value=yy#xx.no, type=work}],
foresatt_navn=[{type=work, value=Xx}, {value=Xy, type=work}, {type=work, value=Yy}]
}
}
What I would like to get: xx#xx.no, xy#xx.no, yy#xx.no
I have tried several things, but I'm afraid I'm not experienced enough.
var epost = foresatt.foresatt_epost;
Results in: TypeError: Cannot read property 'foresatt_epost'
var epost = foresatt('foresatt_epost');
Results in: TypeError: foresatt is not a function
Please advise me, how do I get only the values fram the field 'foresatt epost'?
I believe your goal as follows.
You want to retrieve the values of xx#xx.no, xy#xx.no, yy#xx.no from the following object:
const object = {
Foresatt: {
foresatt_mob: [
{ value: "X#X#X#X#",type: "work"},
{ value: "X#X#X#X#",type: "work"},
{ value: "X#X#X#X#",type: "work"},
],
foresatt_epost: [
{ value: "xx#xx.no", type: "work"},
{ value: "xy#xx.no", type: "work"},
{ value: "yy#xx.no", type: "work"},
],
foresatt_navn: [
{ type: "work", value: "Xx"},
{ type: "work", value: "Xy"},
{ type: "work", value: "Yy"},
]
}
}
In this case, the values can be retrieved from the object.Foresatt.foresatt_epost array.
Sample script:
const object = {}; //Your object
const res = object.Foresatt.foresatt_epost.map(e => e.value);
console.log(res) // Outputs: [ 'xx#xx.no', 'xy#xx.no', 'yy#xx.no' ]
If user.customSchemas is the above object, the script is as follows.
var foresatt = user.customSchemas;
const res = foresatt.Foresatt.foresatt_epost.map(e => e.value);
console.log(res)
If you want to retrieve the value as a comma separated string, you can use res.join(",").
References:
map()
Note:
If there is no guarantee your property will exist in your object, you can do (object.property||[]).map(...) instead of object.property.map(...) to avoid the error Uncaught TypeError: Cannot read property 'forEach' of undefined.
I'm trying to use normalizer to normalize some JSON. My JSON looks like
total: 8029,
items: [
{
id: 1,
name: 'Jacket1',
sku: '123',
upc: '1',
price: '99.99',
images: ['url1', 'url2'],
category: 'clothing',
thumbnail:
'https://cdn.zeplin.io/5969021e44c5978909d5278b/assets/1CE5FF07-E70F-4413-85BF-49C08AA559DE.png',
}, ...
and from the examples, I thought this might work
const itemSchema = new schema.Entity('items')
const itemsSchema = new schema.Entity('result', {
items: [itemSchema],
})
const foo = normalize(fakeDatabase, itemsSchema)
But I end up with one result that is undefined, and that undefined value has some funky stuff in it.
What am I doing wrong?
I don't believe itemsSchema is necessary. Try either:
normalize(fakeDatabase, { items: new schema.Array(itemSchema) })
or
normalize(fakeDatabase, { items: [itemSchema] })
I am working with a dataset that cannot be modified on the server side. So I am trying to setup the local data model on the client in a way that I can easily traverse through the model when updating parts of the data.
Therefore I am trying to create a multi-leveled Map from multi-leveled Maps including Lists, that themselves include Maps, etc. (see schematics at the end of this post).
What I am trying to get is a Map containing other Maps, with the key of the included Map being the value of the object (again please see schematics at the end of this post).
I got it to work on the first level:
const firstLevel = data.toMap().mapKeys((key, value) => value.get('value'));
See it in action here: https://jsfiddle.net/9f0djcb0/4/
But there is a maximum of 3 levels of nested data and I can't get my head around how to get the transformation done. Any help appreciated!
The schematic datasets:
// This is what I got
const dataset = [
{
field: 'lorem',
value: 'ipsum',
more: [
{
field: 'lorem_lvl1',
value: 'ispum_lvl1',
more: [
{
field: 'lorem_lvl2',
value: 'ispum_lvl2',
more: [
{
field: 'lorem_lvl3',
value: 'ispum_lvl3',
}
]
}
]
}
]
},
{
field: 'glorem',
value: 'blipsum'
},
{
field: 'halorem',
value: 'halipsum'
}
];
This is where I want to go:
// This is what I want
const dataset_wanted = {
ipsum: {
field: 'lorem',
value: 'ipsum',
more: {
lorem_lvl1: {
field: 'lorem_lvl1',
value: 'ispum_lvl1',
more: {
lorem_lvl2: {
field: 'lorem_lvl2',
value: 'ispum_lvl2',
more: {
lorem_lvl3: {
field: 'lorem_lvl3',
value: 'ispum_lvl3',
}
}
}
}
}
}
},
glorem: {
field: 'glorem',
value: 'blipsum'
},
halorem: {
field: 'halorem',
value: 'halipsum'
}
};
Retrieve nested structures using "getIn" is beter.
const data = Immutable.fromJS(dataset[0]);
const firstLevel = data.getIn(['more']);
const twoLevel = firstLevel.getIn([0,'more']);
const threeLevel = twoLevel.getIn([0,'more']);
console.log(firstLevel.toJS(),twoLevel.toJS(),threeLevel.toJS());
As for a more generative solution, I re-wrote the answer before to a recursive approach:
function mapDeep(firstLevel) {
return firstLevel.map((obj) => {
if (obj.has('more')) {
const sec = obj.get('more').toMap().mapKeys((key, value) => value.get('value'));
const objNext = mapDeep(sec);
obj = obj.set('more', objNext);
}
return obj;
});
}
The first level still needs to be mapped manually before.
const firstLevel = data.toMap().mapKeys((key, value) => value.get('value'));
const secondLevel = mapDeep(firstLevel);
Again, see it in action: https://jsfiddle.net/9f0djcb0/12/
This is good enough for me for now. Still feels like this can be solved smarter (and more performant).. Cheers :)
So after some time passed I came up with a solution that works for me:
let sec, third, objThird;
// 1st level: simple mapping
const firstLevel = data.toMap().mapKeys((key, value) => value.get('value'));
// 2nd level: walk through updated firstLevel's subobjects and do the mapping again:
const secondLevel = firstLevel.map((obj) => {
if (obj.has('more')) {
sec = obj.get('more').toMap().mapKeys((key, value) => value.get('value'));
// 3nd level: walk through updated secondLevel's subobjects and do the mapping again:
objThird = sec.map((o) => {
if (o.has('more')) {
third = o.get('more').toMap().mapKeys((key, value) => value.get('value'));
o = o.set('more', third);
}
return o;
});
obj = obj.set('more', objThird);
}
return obj;
});
See it in action here: https://jsfiddle.net/9f0djcb0/7/
This has been working nicely so far, thur pretty hard-coded. If anyone has a more elegant solution to this, I am happy to learn about it!
I have a medium sized xml ~ 5mb that needs to be converted to csv.
Obviously wont go for reinventing the wheel,
so a two layer approach -
1> xml to json
2> json to csv
My current code is :
const xml_obj = {}
const htt = require('http-status-code-node');
var fs = require('fs');
var xml2js = require('xml2js');
var converter = require('json-2-csv');
xml_obj["convert"] = (req, res, next) => {
var parser = new xml2js.Parser();
fs.readFile(__dirname + '/directoryexport.xml', function (err, data) {
parser.parseString(data, function (err, result) {
console.log('Done');
var callback = function (err, ycsv) {
if (err) return console.log(err);
///
res.setHeader('Content-Disposition', 'attachment; filename=testing.csv');
res.set('Content-Type', 'text/csv');
res.status(200).send(result);
///
}
var documents = [];
documents.push(result)
converter.json2csv(documents, callback);
})
});
}
module.exports = xml_obj.convert
However the xml being nested gives a multi layered json which the yields a single string instead of a proper delimited csv..
The current output CSV
The Original xml
The XML structure
The Json I get on converting xml
Also as per the documentation of the json to csv converter
if the input json is in a proper structure like :
[
{
Make: 'Nissan',
Model: 'Murano',
Year: '2013',
Specifications: {
Mileage: '7106',
Trim: 'S AWD'
}
},
{
Make: 'BMW',
Model: 'X5',
Year: '2014',
Specifications: {
Mileage: '3287',
Trim: 'M'
}
}
];
This yields a very nicely formatted csv like this : Example Perfect CSV From JSON
Edit 1 :
The format I'm looking for is somewhat like,
It's important to capture all parent organization and organizationalUnit details for each person node.
For example,
organizationalUnit UUID "b3b05b77-a8a7-43ed-ab74-b7d898c60296" should
produce a CSV lines like:
"Mr Shayne Howard","Howard","Shayne","Mr","","Branch Manager","(02) 6121 5492","","Level 1, 12 Mort Street, Canberra, ACT, 2601","Shayne.Howard#employment.gov.au","","b43e0864-1b9a-40f0-8049-c90af5f9141c","","GPO Box 9880 CANBERRA ACT 2601 Australia",1392,"","Department of Employment","","1300 488 064","","","","http://www.employment.gov.au","GPO Box 9880, Canberra ACT 2601","EMPLOYMENT"
"Mr Luke de Jong","De Jong","Luke","Mr","","Branch Manager, General Counsel","(02) 6240 0909",""(02) 6123 5100"","","Luke.deJong#employment.gov.au","","58a503a8-ce8b-41c0-b690-b9f9efd98a89","","GPO Box 9880 CANBERRA ACT 2601",1393,"","Department of Employment","","1300 488 064","","","","http://www.employment.gov.au","GPO Box 9880, Canberra ACT 2601","EMPLOYMENT"
Edit 2 :
Flattening out the json is a good idea, but its not capturing the entire data.
Using the camaro nodejs module with the following template :
persons: ['//person', {
root_organization_name: '../../../../name',
main_organization_name: '../../../name',
main_organization_website: '../../../website',
fullName: 'fullName',
familyName: 'familyName',
firstName: 'firstName',
personalTitle: 'personalTitle',
title: 'title',
person_phone: 'phone',
person_location: 'location',
person_fax: 'fax',
otherRolesDN: 'otherRolesDN',
person_mail: 'mail',
informationPublicationScheme: '../informationPublicationScheme',
publications: '../../publications',
annualReport: '../../annualReport',
mediaReleases: '../../mediaReleases',
organizationUnit_1_name: '../../name',
organizationUnit_1_description: '../../description',
organizationUnit_1_location: '../../location',
organizationUnit_1_phone: '../../phone',
organizationUnit_1_fax: '../../fax',
organizationUnit_1_website: '../../website',
organizationUnit_2_name: '../name',
organizationUnit_2_location: '../location',
organizationUnit_2_phone: '../phone',
organizationUnit_2_fax: '../fax',
organizationUnit_2_website: '../website',
occupantName: './role/occupantName',
roleName: './role/roleName',
occupantUUID: './role/occupantUUID',
role_phone: './role/phone',
role_fax: './role/fax',
role_location: './role/location',
role_mail: './role/ mail'
}]
How could I also get the roles array.
Also current csv gets some rows of data in wrong columns :
Wrong csv after camaro
Any tips on how to make this work with my input.
Because the output of json structure is deep, if you want it to convert to csv properly, you would have to flatten it.
Seems like you only interest in deepest level. here's an example, if you want to add more data, feel free to add to the template
const transform = require('camaro')
const tocsv = require('json2csv')
const fs = require('fs')
const xml = fs.readFileSync('so.xml', 'utf-8')
const template = {
persons: ['//person', {
root_organization_name: '../../../../name',
main_organization_name: '../../../name',
main_organization_website: '../../../website',
fullName: 'fullName',
familyName: 'familyName',
firstName: 'firstName',
personalTitle: 'personalTitle',
title: 'title',
person_phone: 'phone',
person_location: 'location',
person_fax: 'fax',
otherRolesDN: 'otherRolesDN',
person_mail: 'mail',
informationPublicationScheme: '../informationPublicationScheme',
publications: '../../publications',
annualReport: '../../annualReport',
mediaReleases: '../../mediaReleases',
organizationUnit_1_name: '../../name',
organizationUnit_1_description: '../../description',
organizationUnit_1_location: '../../location',
organizationUnit_1_phone: '../../phone',
organizationUnit_1_fax: '../../fax',
organizationUnit_1_website: '../../website',
organizationUnit_2_name: '../name',
organizationUnit_2_location: '../location',
organizationUnit_2_phone: '../phone',
organizationUnit_2_fax: '../fax',
organizationUnit_2_website: '../website',
roles: ['../role', {
occupantName: 'occupantName',
roleName: 'roleName',
occupantUUID: 'occupantUUID',
role_phone: 'phone',
role_fax: 'fax',
role_location: 'location',
role_mail: ' mail'
}]
}]
}
const result = transform(xml, template)
console.log(JSON.stringify(result.roles, null, 4))
Example of output json (use json2csv to convert to csv if you want)