Re-ordering of JSON data - javascript

I am new to JSON and want to re-structure my JSON response,
JSON-structure now-
{
"map": {
"Cityname": "[Jammu, Srinagar]",
"Pincode": "[180001, 190001]"
}
}
How I need it to be-
[
{ Cityname: "Jammu", Pincode: 180001},
{ Cityname: "Srinagar", Pincode: 190001}
]
Is there a way to do so, I searched for some possible solutions but was unable to do so.

Here is a Dynamic way of doing so, should work with any json string with the same layout. I fixed some of your json string.
// first we use JSON.parse() to turn a json string into a JS Object.
const data = JSON.parse(`{ "map": { "cityName": ["Jammu", "Srinagar"], "pinCode": [180001, 190001]}}`);
// cities will hold our city objects
const cities = [];
// Object.entries(theObject) returns theObjects keys and values as an array
// EG:
// [[key, value], [key, value]]
// forEach() loops through each key, value pair.
// Because we know that we are going to get a key value pair,
// I destructured the array into it's key and values using:
// [key, values] = [properties];
Object.entries(data.map).forEach((prop) => {
const [key, values] = prop;
values.forEach((value, index) => {
// now we check that the current index is an object.
// we do this because we can't add a property and value otherwise.
if(typeof cities[index] != "object"){
cities[index] = {};
}
// now we set the current value
cities[index][key] = value;
})
})
console.log(cities);

Your JSON response, its not quite logical because there is not mapping between city and pincode. I assumed that cityname and pincode are in the same order in the arrays. I used exact json structure you provided in the question.
You can skip additional steps substring and parse if your json data have correct data types (Cityname string array / Pincode int array).
const json = {
"map": {
"Cityname": "[Jammu, Srinagar]",
"Pincode": "[180001, 190001]"
}
}
const someFunc = () => {
let output = [];
const {Cityname, Pincode} = json.map;
const citynameArray = (Cityname.substring(1, Cityname.length-1)).split(",");
const pincodeArray = JSON.parse(Pincode);
citynameArray.map((v,i) => {
output.push({Cityname: v, Pincode: pincodeArray[i]})
});
return output;
}
console.log(someFunc());

Related

SheetJS specify header order with json_to_sheet

I am using SheetJS in angular to export json as .xlsx file. For reference the json could be as follows:
[{
"ID": "E111",
"Name": "John",
"LastLogin": "2022-02-12"
},
{
"ID": "E112",
"Name": "Jake",
"Score": 22
"LastLogin": "2022-02-12"
}]
Note: The keys to the object are unknown, and can vary. The only known keys are ID and LastLogin.
I am using the following function to export
public exportAsExcelFile(json: any[], excelFileName: string): void {
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
console.log('worksheet',worksheet);
const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] };
const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
this.saveAsExcelFile(excelBuffer, excelFileName);
}
private saveAsExcelFile(buffer: any, fileName: string): void {
const data: Blob = new Blob([buffer], {
type: EXCEL_TYPE
});
FileSaver.saveAs(data, fileName + '_export_' + new Date().getTime() + EXCEL_EXTENSION);
}
The resulting excel looks like this
I want LastLogin to be the last column no matter the object. Is there a way to achieve this? I am pretty new to this, so any help is appreciated.
The behaviour of SheetJS here is to take the order of column headers for the Excel data from the first row, and then as new object keys are encountered then for the matching row header to be added at the end.
To control this behaviour to get the output formatted the way you want, you can process the input json before calling XLSX.utils.json_to_sheet.
Define this function:
function restructureObjectForSheet(obj) {
// for each object in the array put the keys in a new array
// flatten that array
// there will be duplicate names which can be removed with Set
// turn it back into an array
const uniqKeys = Array.from(new Set(obj.map(o => Object.keys(o)).flat()));
// remove LastLogin from this array
// then put LastLogin at the end of the array
const endKey = "LastLogin";
const rowHeaders = uniqKeys.filter(k => k !== endKey).concat(endKey);
// process the original data into a new array
// first entry will define row headers in Excel sheet
const newData = obj.map(o => {
return rowHeaders.reduce((a, c) => {a[c] = o[c] ; return a}, {});
});
return newData;
}
I've commented the code, but the essential features are:
get an array of all the unique keys across all the objects in your input array (your json variable)
ensure LastLogin is the last element of the array
create one new object per input object and where the original data does not have the property (e.g. Score) then the value is undefined
Now, in your exportAsExcelFile method, just make this adjustment before the 1st line:
const newJson = restructureObjectForSheet(json);
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(newJson );

How to change an array to a JSON format with javascript

I have an array which is in the below format
node=['ADSss|623', 'sss|635']
I want this to be in a json format as below
[
{
"623": "ADSss"
},
{"635": "sss"
}
]
Is there a simple way to achieve this? it is possible with map function, but i felt it is adding up too many lines of code
Assuming that you array only contain string of format ${value}|${key}, you can map those to an object:
const result = node.map((arrayEl) => {
// Split you array string into value and keys
const [value, key] = arrayEl.split('|');
// return an object key: value
return {[key]: value}
});
console.log(result);
In one line :
const node=['ADSss|623', 'sss|635'];
const json = node.reduce((p, n) => ({ ...p, [n.split('|')[1]]: n.split('|')[0] }), {});
const jsonArray = node.map(n => ({ [n.split('|')[1]]: n.split('|')[0] }));
console.log(json);
console.log(jsonArray);

How can I push objects to arrays with similar names?

I have a form that I can create duplicate sections. Right now when the form is submitted the form spits out one giant object. I'm working on a filter function that can filter out the duplicate sections and organize the duplicates in array so it works with my API.
e.g.
// base object
{
question_10: ""
question_10a: ""
question_11_checkbox: false
question_11_checkbox_copy_0: false
question_11_checkbox_copy_1: true
question_11_text: "110 Monroe St"
question_11_text_copy_0: "186 Aspen Road"
question_12_checkbox: false
question_12_checkbox_copy_0: false
question_12_text: "New York"
question_12_text_copy_0: "South Orange"
...
}
// what I want is
{
question_10: ""
question_10a: ""
question_11_checkbox: false
question_11_checkbox_copies: [
{ question_11_checkbox_copy_0: false }
{ question_11_checkbox_copy_1: true }
]
question_11_text: "101 Monroe St"
question_11_text_copies: [
{ question_11_text_copy_0: "186 Aspen Road"}
]
question_12_checkbox: false
question_12_checkbox_copies: [
{ question_12_checkbox_copy_0: false}
]
question_12_text: "New York"
question_12_text_copies: [
{ question_12_text_copy_0: "South Orange"}
]
...
}
So far I've been able to filter out the copies from the original object and create the arrays for the copies
// filter out copy keys
const copiesKey = Object.keys(data).filter(key => key.includes('copy'));
const copy = {};
// create arrays for copies
copiesKey.map(copiedQuestion => {
if (!(`${copiedQuestion.slice(0, copiedQuestion.length - 7)}_copies` in copy)) {
copy[`${copiedQuestion.slice(0, copiedQuestion.length - 7)}_copies`] = [];
}
});
Where I'm stuck is it's not clear how to match the object to the appropriate array and push it.
e.g.
question_11_text_copies: [
{ question_11_text_copy_0: "186 Aspen Road" }
]
So far I've tried to slice the last three keys of the copy_0 object and match the key to the array name by using array.filter, but that didn't work as expected.
How can I match the 'copy_n' objects to the appropriate array and push those objects to the array?
This might be a bit old fashioned, but using a for...in loop in combination with regex pattern matching against the key might be the clearest solution.
const data = {
question_10: "",
question_10a: "",
question_11_checkbox: false,
question_11_checkbox_copy_0: false,
question_11_checkbox_copy_1: true,
question_11_text: "110 Monroe St",
question_11_text_copy_0: "186 Aspen Road",
question_12_checkbox: false,
question_12_checkbox_copy_0: false,
question_12_text: "New York",
question_12_text_copy_0: "South Orange",
};
const copy_n = /^(.*)_copy_(\d+)$/;
const result = {};
for (const key in data) {
const value = data[key];
const match = key.match(copy_n);
if (match) {
const copies_key = `${match[1]}_copies`;
const index = parseInt(match[2], 10);
result[copies_key] ||= [];
result[copies_key][index] = { [key]: value };
} else {
result[key] = value;
}
}
console.log(result);
The pattern /^(.*)_copy_(\d+)$/ matches anything that ends with _copy_ followed by 1 or more decimals. Everything before _copy_ is placed in capture group 1, the decimals behind _copy_ are placed in capture group 2.
If there is no match (else scenario) we simply assign the value to the same key.
If there is a match (if scenario) we first determine the collection key (copies_key) and the index to use. We then check if result[copies_key] is already set (we check if it's a truthy value to be precise). If it's not, we assign it to an empty array. After that we use the index to assign an object to the correct index in the array.
You can reduce to object's entries to a new object. For each key find if it has a base key (the one before "copy") using a RegExp with lookahead. If it doesn't has a base key, add it directly to the accumulator. If it does, add it to the appropriate "copies" array (initialize it if needed using Logical nullish assignment ??=) (TS playground).
const fn = obj => Object.entries(obj)
.reduce((acc, [k, v]) => {
const [baseKey] = k.match(/.*(?=_copy_)/) ?? [] // get the key from an item with copy
if(!baseKey) acc[k] = v // if no baseKey add the item to the accumulator
else {
const copiesKey = `${baseKey}_copies`
acc[copiesKey] ??= [] // if no copies key add a new one to the accumulator
acc[copiesKey].push({ [k]: v }) // create an object and push to the current copies key
}
return acc
}, {})
const obj = {"question_10":"","question_10a":"","question_11_checkbox":false,"question_11_checkbox_copy_0":false,"question_11_checkbox_copy_1":true,"question_11_text":"110 Monroe St","question_11_text_copy_0":"186 Aspen Road","question_12_checkbox":false,"question_12_checkbox_copy_0":false,"question_12_text":"New York","question_12_text_copy_0":"South Orange"}
const result = fn(obj)
console.log(result)

Typescript filter to select object with value of specific words

I have an array of objects in my DB records.
The record:
{"ID123":{"FileID":"12345","FileName":"ABCFile_ver5_44453.PDF"},"DocID":6009,"DocFormat":"PDF"}
The format to store the filename in my DB is always with "FileName": "actual_fileName.PDF".
I want to only get the object with "FileName":"....", to only display the filename instead of other objects.
This is my code:
getValue(): Atts[] | any {
if (this.isEmptyValue()) {
return [];
}
return !this.useObject ? this.value : Object.entries(this.value).map(([key, value]) => ({ key, value })).filter(value=);
}
How do I filter the object that contains "FileName" so that I can display the filename in my application?
I'm stuck at the filter method.
I had to reduce your code a little to a minimal reproducible example, and I made the assumption that this.value in your Object.entries is the entire DB record that you have listed above. I converted this to an object so I could process it in my code. So, your mileage may vary if my assumption was incorrect.
let obj = {
"ID123": {
"FileID": "12345",
"FileName": "ABCFile_ver5_44453.PDF"
},
"DocID": 6009,
"DocFormat": "PDF"
};
let result = { "FileName": Object.entries(obj).map(([key, value]) => ({
key,
value
})).filter(keyValuePair => {
if (keyValuePair.value.FileName) {
return true;
}
})[0].value.FileName };
This returns:
{
"FileName": "ABCFile_ver5_44453.PDF"
}
Your filter step is filtering an array of key value pairs, so when filtering, you need to return true only if the 'value' is an object that has a property of FileName.
EDIT:
I realized that the way I wrote this will not work if the returned object from the array does not have value (or is undefined), so it's probably a better idea to store this in a variable first, and then return an object based on that varible, like so:
let fileNameObj = Object.entries(obj).map(([key, value]) => ({
key,
value
})).filter(keyValuePair => {
if (keyValuePair && keyValuePair.value.FileName) {
return true;
}
})[0];
if (fileNameObj && fileNameObj.FileName) {
let result = { "FileName": fileNameObj.FileName };
}

Return an entire object where filter and includes matches

I currently have several objects like so:
// not correct syntax just showing what members is
const members = "-Lz8YxIL-XJotORQ3Bk1": Object {
"address": "14539 Fasdfadsfrd Ave",
"bDay": "10/01/2004",
"city": "Norwalk",
"email": "wasdfsadfga2#yahoo.com",
"first": "Frank",
}, ...
I also have an object with a few UIDs like so:
const attendanceGroup.members = {
"-MxQvetKWRGNVO6EWIko": "-Lz8YxHiwp8QZW3TqAFn",
"-MxSvxI-D53qlXtp1nzT": "-Lz8YxHiwp8QZW3TqAFn",
}
I would like to get the object returned of each object where the uid matches.
I'm doing it like so:
const groupMembers = Object.keys(members).filter(item => {
return Object.values(attendanceGroup.members).includes(item);
});
But this only returns the keys of the matching uid. I would like the whole object.
How can I accomplish this?
Object.entries will give you the key, value pairs:
const attendanceGroupMembers = new Set(Object.values(attendanceGroup.members));
const groupMembers = Object.entries(members)
.filter(([key]) => attendanceGroupMembers.has(key))
.map(([_, members]) => members);

Categories

Resources