How to iterate nested json object? - javascript

I have a nested JSON Object and i want to iterate that.
JSON Response
{
"specifications": {
"IP6": {
"name": "Devices",
"productSubType": "Device",
"productSpecificationType": "Phones"
}
},
"offers": {
"US-PRE-IPHONE-CASE": {
"path": "productDetails/IP6",
"familyData": {
"0": "Missing Family Level data Should be here"
},
"facets": [],
"type": [],
"offers": {
"US-PRE-HG-PH-IP6": {
"hashDigest": "cf23df2207d99a74fbe169e3eba035e633b65d94",
"offerName": "offerNameString",
"productName": "iPhone 6 Case Mate Naked Tough Case - Clear",
"productOfferings": {
"ratings": "4.5",
"noOfReviews": "2010"
},
"offerStatus": {},
"displayPriority": "100200",
"descriptions": {
"shortDescription": "Iphone Decription ",
"longDescription": "longDescriptionStri6 descriptionng",
"alternativeDescription": "alternativeDescriptionString",
"reprsentativeDescription": ""
},
"specifications": [
"someSpecificationId1"
],
"brand": "Apple",
"productType": "Device",
"productSubType": "Phone",
"offerType": "",
"offerSubType": "",
"compatibility": {},
"classification": [],
"images": {
"thumbanail": {
"imagePath": "http://s.tmocache.com/images/png/products/accessories/SUPM43270/SUPM43270-small.png"
}
},
"equipmentCharacteristics": {},
"offerVariants": {},
"type": "hard-good",
"offers": [],
"family": "IP6",
"pricePoints": {
"withServicePrice16GBNEW": {
"displayPriority": "1001",
"pricingMessage": "device price with service activation",
"price": "34.99",
"discounts": {}
}
},
"dynamicPricingData": {},
"inventoryData": {
"SKUGOLD16GBN": {
"availibility": "Pre-order now!",
"availableTimeline": ""
}
}
}
}
}
}
}
Now as you see there are nested JSON objects in this and I want the value of
productName
shortDescription
imagePath
availibility
What I have tried is
function change(){
var acc = response; //response is JSON Object mentioned above
var accArray = [];
var accArray1 = [];
for (var obj in acc.specifications){
accArray.push(obj);
}
alert(accArray[0]);
for (var obj in accArray[0].offers){
accArray1.push(obj);
}
alert(accArray1[0]);
}
I am able to get the first object the first alert output is
IP6
but when I am trying to iterarte the IP6 object in same way the output is
undefined
I want to fetch all the 4 values as I mentioned above and then put them in an array.

As Grundy pointed out in his comment, obj in your code is the key of properties/items in specifications object. That means 'obj' is just a string.
To get reference to the object, change your code as below:
for(var obj in acc.specifications){
accArray.push(acc.specifications[obj]);
}
For better readability change obj to key

You can use for..in loop and recursion.
function find(obj, fieldName){
if(Array.isArray(obj)){
for(var i=0, len=obj.length;i<len;i++){
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}else{
if(typeof obj !== "object") return {isFind:false};
for(var i in obj){
if(i === fieldName) return {isFind:true, value:obj[i]};
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}
return {isFind:false};
}
this function return object with field isFind for case when available value can be null or undefined
var obj = {
"specifications": {
"IP6": {
"name": "Devices",
"productSubType": "Device",
"productSpecificationType": "Phones"
}
},
"offers": {
"US-PRE-IPHONE-CASE": {
"path": "productDetails/IP6",
"familyData": {
"0": "Missing Family Level data Should be here"
},
"facets": [],
"type": [],
"offers": {
"US-PRE-HG-PH-IP6": {
"hashDigest": "cf23df2207d99a74fbe169e3eba035e633b65d94",
"offerName": "offerNameString",
"productName": "iPhone 6 Case Mate Naked Tough Case - Clear",
"productOfferings": {
"ratings": "4.5",
"noOfReviews": "2010"
},
"offerStatus": {},
"displayPriority": "100200",
"descriptions": {
"shortDescription": "Iphone Decription ",
"longDescription": "longDescriptionStri6 descriptionng",
"alternativeDescription": "alternativeDescriptionString",
"reprsentativeDescription": ""
},
"specifications": [
"someSpecificationId1"
],
"brand": "Apple",
"productType": "Device",
"productSubType": "Phone",
"offerType": "",
"offerSubType": "",
"compatibility": {},
"classification": [],
"images": {
"thumbanail": {
"imagePath": "http://s.tmocache.com/images/png/products/accessories/SUPM43270/SUPM43270-small.png"
}
},
"equipmentCharacteristics": {},
"offerVariants": {},
"type": "hard-good",
"offers": [],
"family": "IP6",
"pricePoints": {
"withServicePrice16GBNEW": {
"displayPriority": "1001",
"pricingMessage": "device price with service activation",
"price": "34.99",
"discounts": {}
}
},
"dynamicPricingData": {},
"inventoryData": {
"SKUGOLD16GBN": {
"availibility": "Pre-order now!",
"availableTimeline": ""
}
}
}
}
}
}
}
function find(obj, fieldName){
if(Array.isArray(obj)){
for(var i=0, len=obj.length;i<len;i++){
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}else{
if(typeof obj !== "object") return {isFind:false};
for(var i in obj){
if(i === fieldName) return {isFind:true, value:obj[i]};
var nested = find(obj[i],fieldName);
if(nested.isFind) return nested;
}
}
return {isFind:false};
}
var result = ['productName','shortDescription','imagePath','availibility'].map(function(el){ return find(obj,el).value});
document.getElementById('r').innerHTML = JSON.stringify(result,null,2);
<pre id='r'></pre>

Your json code is a complex data binding structure. It same like c# complex data binding. So you need to call the obj by through it call name.
for eg:
var data = {"ex":{"a":{"a1":"a1","a2":"a2"},"b":{"b1":"b1","b2":"b2"}}}
so data is a class and it includes "ex" object
data returns =>Object {ex: Object}
if you need to access "a" or "b" object means , you need to access through the"ex" object.
for eg:
data.ex.a => Object {a1: "a1", a2: "a2"}
in your code
for(var obj in acc.specifications){
accArray.push(obj);
}
obj only push 1st element of acc.sppectification object.
So please try this.
foreach(var obj acc.specification){
arr1.push(acc.specification[obj])
}
foreach (var obj acc.offers){
arr2.push(acc.offers[obj])
}

Related

how to transform a source object into another object?

I am trying to transform an object or in other words I am trying change text of an object.
I have two object one is source and another is mapped object.using mapped object I want o transform the source object.
example
source object
{
"links": {
"Offering": {
"id":"offering-id",
"iconUrl": "",
"links": [
{
"text": "School",
"id":"school-id",
"onclickUrl": "https://www.school.com/school/"
},
{
"text": "happy",
"onclickUrl": "https://www.happy.com/help/",
"id":"happy-id"
}
]
},
"luck": {
"iconUrl": "",
"id":"hospital-id",
"links": [
{
"text": "Student",
"id":"student-id",
"onclickUrl": "https://www.student.com/school/"
}
]
}
}
}
mapped -object
let mappingObj = {
"hospital-id":"hospital-change",
"school-id":"school-change-xx",
"offering-id":"Offering-changes-map",
"happy-id":"happy-cs",
"student-id":"nnnnnn"
}
expectedout output
let expectedOutput = {
"links": {
"Offering-changes-map": {
"id":"offering-id",
"iconUrl": "",
"links": [
{
"text": "school-change-xx",
"id":"school-id",
"onclickUrl": "https://www.school.com/school/"
},
{
"text": "happy-cs",
"onclickUrl": "https://www.happy.com/help/",
"id":"happy-id"
}
]
},
"hospital-change": {
"iconUrl": "",
"id":"hospital-id",
"links": [
{
"text": "nnnnnn",
"id":"student-id",
"onclickUrl": "https://www.student.com/school/"
}
]
}
}
}
I have id's in source obj same id's are present in mapped obj.using this I want to change the text of all properties and keys.
I tried like that here is my whole code
https://jsbin.com/zaxoyineme/edit?js,console,output
let result = {};
function recursiveFn(Obj){
for(let i in Obj){
result[mappingObj[Obj[i].id] || i]=Obj[i];
if(typeof Obj[i]==="object"){
console.log('--obj--');
recursiveFn(Obj[i]);
}else if(Object.prototype.toString.call(Obj[i]) === '[object Array]'){
console.log('--Array--');
for(var k =0 ;k<Obj[i].length;k++){
Obj[i].text = result[mappingObj[Obj[i].id] || Obj[i].text]
}
}
}
}
for(let i in Obj){
result[mappingObj[Obj[i].id] || i]=Obj[i];
if(typeof Obj[i]==="object"){
console.log('----');
recursiveFn(Obj[i])
}else if(Object.prototype.toString.call(Obj[i]) === '[object Array]'){
console.log('--Array--');
for(var k =0 ;k<Obj[i].length;k++){
Obj[i].text = result[mappingObj[Obj[i].id] || Obj[i].text]
}
}
}
console.log(JSON.stringify(result))
I am using recession to get the expected output.but I am not able to get the expected output
Your code is way to complicated for what you are trying to do. From What I understood this should probably do your job.
let Obj = {
"links": {
"Offering": {
"id":"offering-id",
"iconUrl": "",
"links": [
{
"text": "School",
"id":"school-id",
"onclickUrl": "https://www.school.com/school/"
},
{
"text": "happy",
"onclickUrl": "https://www.happy.com/help/",
"id":"happy-id"
}
]
},
"luck": {
"iconUrl": "",
"id":"hospital-id",
"links": [
{
"text": "Student",
"id":"student-id",
"onclickUrl": "https://www.student.com/school/"
}
]
}
}
}
let mappingObj = {
"hospital-id":"hospital-change",
"school-id":"school-change-xx",
"offering-id":"Offering-changes-map",
"happy-id":"happy-cs",
"student-id":"nnnnnn"
}
// expected output
let expectedOutput = {
"links": {
"Offering-changes-map": {
"id":"offering-id",
"iconUrl": "",
"links": [
{
"text": "school-change-xx",
"id":"school-id",
"onclickUrl": "https://www.school.com/school/"
},
{
"text": "happy-cs",
"onclickUrl": "https://www.happy.com/help/",
"id":"happy-id"
}
]
},
"hospital-change": {
"iconUrl": "",
"id":"hospital-id",
"links": [
{
"text": "nnnnnn",
"id":"student-id",
"onclickUrl": "https://www.student.com/school/"
}
]
}
}
}
let result = {};
let midResult = {};
function updateObject (Obj) {
let i = 0;
let parseArray = Object.keys(Obj.links);
for (i =0; i < parseArray.length; i++) {
let id = parseArray[i]
let matchingId = mappingObj[Obj.links[id].id]
midResult[matchingId] = Object.assign({}, Obj.links[id])
}
}
updateObject(Obj);
result.links = Object.assign({}, midResult)
console.log(result)

Normalize JSON to a custom schema

I have an array of objects with the following format
var arr = [
{
"productId": "123456",
"productName": "Test Product 1",
"description": [
"This is delicious",
"Suitable for vegetarian"
],
"attributes": {
"internalId": "091283"
"category": "Dairy"
},
"order": 1
}
];
And I am trying to map into something like below
[
[{
{
"name": "productId",
"value": "123456"
},
{
"name": "productName",
"value": "Test Product 1"
},
{
"name": "description",
"value": ["This is delicious", "Suitable for vegetarian"]
},
{
"name": "attributes",
"value": {
{
"name": "internalId",
"value": "091283"
},
{
"name": "category",
"value": "Dairy"
}
}
},
{
"name": "order",
"value": 1
}
}]
]
I tried mapping simple properties before going further and now stuck at getting only the last property of each object in the loop.
Suppose I don't know what are the format of incoming data and how can I normalize the JSON object to the format I want?
normalizeJson = (array) => {
for(i = 0; i < array.length; i++){
normalizedJson[i] = {};
Object.keys(array[i]).forEach(key => {
if (array[i][key] && typeof array[i][key] === "object") {
// normalizeJson(obj[key]);
// console.log(key + ' is object');
return;
} else {
o = {};
o["name"] = key;
o["value"] = array[i][key];
normalizedJson[i] = o;
// normalizedJson[i]["name"] = key;
// normalizedJson[i].value = array[i][key];
// console.log(key);
return;
}
});
}
console.log(normalizedJson);
};
Or is there any library I can use in order to achieve this?
Try this
var obj = [
{
productId: "123456",
productName: "Test Product 1",
description: ["This is delicious", "Suitable for vegetarian"],
attributes: {
internalId: "091283",
category: "Dairy",
},
order: 1,
},
];
function normalizeObject(obj) {
var result = [];
if (Array.isArray(obj)) {
for (let i of obj) {
result.push(normalizeObject(i));
}
} else if (typeof obj == "object") {
for (let i of Object.keys(obj)) {
result.push({ name: i, value: normalizeObject(obj[i]) });
}
} else {
return obj;
}
return result;
}
console.log(JSON.stringify(normalizeObject(obj), null, 2));
This looping method called recursion. Which is loop by calling function itself.

Lodash keyBy for multiple nested level arrays

I have an array of object, and i want to convert it into a map of key value pairs with the id as the key. However, I want to do it for both the root level and within the recipes attribute.
Array resp:
[
{
"id": "1",
"recipes": [
{
"id": 4036
},
{
"id": 4041
}
]
},
{
"id": "2",
"recipes": [
{
"id": 4052
},
{
"id": 4053
}
]
}
]
I came across _.keyBy() which maps an attribute as the key, but it doesn't allow nested levels.
Function:
var respObj = _.keyBy(resp, 'id');
Is there an elegant solution to massage resp to make all the objects nested within the array use id as key?
thanks!
you can do it with _.keyBy and _.mapValues
_.chain(resp)
.keyBy('id')
.mapValues(function(item) {
item.recipes = _.keyBy(item.recipes, 'id');
return item;
})
.value();
This is a generic solution that runs _.keyBy recursively on arrays, and the objects inside them:
function deepKeyBy(arr, key) {
return _(arr)
.map(function(o) { // map each object in the array
return _.mapValues(o, function(v) { // map the properties of the object
return _.isArray(v) ? deepKeyBy(v, key) : v; // if the property value is an array, run deepKeyBy() on it
});
})
.keyBy(key); // index the object by the key
}
I've added another level of data in the example (ingredients):
function deepKeyBy(arr, key) {
return _(arr)
.map(function(o) {
return _.mapValues(o, function(v) {
return _.isArray(v) ? deepKeyBy(v, key) : v;
});
})
.keyBy(key);
}
var arr = [{
"id": "1",
"recipes": [{
"id": 4036,
"ingerdients": [{
"id": 5555555
}, {
"id": 5555556
}, {
"id": 5555557
}]
}, {
"id": 4041
}]
}, {
"id": "2",
"recipes": [{
"id": 4052
}, {
"id": 4053
}]
}];
var result = deepKeyBy(arr, 'id');
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
You could get a flattened collection of recipes, concatenate the response and then key by id:
var result = _.chain(resp)
.flatMap('recipes')
.concat(resp)
.keyBy('id')
.value()
The flatMap call will pluck all the recipes from the response and flatten the arrays so we're left with this:
[
{ "id": 4036 },
{ "id": 4041 },
{ "id": 4052 },
{ "id": 4053 }
]
The response is then appended to this array using concat so we then have:
[
{ "id": 4036 },
{ "id": 4041 },
{ "id": 4052 },
{ "id": 4053 },
{ "id": "1", recipes: ... },
{ "id": "2", recipes: ... }
]
Finally we use keyBy to get the required structure .
var resp = [
{
"id": "1",
"recipes": [
{
"id": 4036
},
{
"id": 4041
}
]
},
{
"id": "2",
"recipes": [
{
"id": 4052
},
{
"id": 4053
}
]
}
]
var result = _.chain(resp)
.flatMap('recipes')
.concat(resp)
.keyBy('id')
.value()
document.getElementById('result').textContent = JSON.stringify(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<p>
<pre id="result"></pre>
</p>

Backbone - Parse object into models for collection

I am getting an object with several nested objects back from a collection.fetch() call. Is there a way to parse these sub-objects and pass them into the collection as individual models?
Here is an example of the data I am getting back. I'd like to pass this into the collection as 3 models when it is fetched from the server.
Here is a fiddle to what I am trying but I am not sure of the best way to go about this: http://jsfiddle.net/L8ov7oo5/
Data from server:
{
"slides": {
"date": "August 21, 2014",
"Author": "Luke Skywalker",
"slide1": {
"content": {
"headline": "headline 1",
"desc": "description for slide 1",
"image": [
{
"url": "http://placekitten.com/100/100",
"type": "thumbnail",
"alt": "imageofakitten"
}
]
}
},
"slide2": {
"content": {
"headline": "headline2",
"desc": "descriptionforslide2",
"image": [
{
"url": "http: //placekitten.com/125/125",
"type": "thumbnail",
"alt": "imageofakitten"
}
]
}
},
"slide3": {
"content": {
"headline": "headline3",
"desc": "descriptionforslide3",
"image": [
{
"url": "http: //placekitten.com/150/150",
"type": "thumbnail",
"alt": "imageofakitten"
}
]
}
}
}
}
Example of model I'd like to pass to the collection:
{
"slide1": {
"content": {
"headline": "headline 1",
"desc": "description for slide 1",
"image": [
{
"url": "http://placekitten.com/100/100",
"type": "thumbnail",
"alt": "imageofakitten"
}
]
}
}
}
Here is my code, but when I log the results of the collection.fetch() I don't see 3 models in the collection.
var MyModel = Backbone.Model.extend({});
var MyCollection = Backbone.Collection.extend({
model: MyModel,
url: 'https://api.mongolab.com/api/1/databases/parse-test/collections/parse-test-collection?apiKey=x5akja343lqkja',
parse: function (response) {
console.log('response', response);
for (var prop in response[0].slides) {
if ( response[0].slides.hasOwnProperty(prop) ) {
// Try to add an object as a 'model' to the 'collection'
this.add( response[0].slides[prop] );
}
}
return response;
}
});
var myCollection = new MyCollection();
myCollection.fetch().done(function () {
// Expecting to see 3 models in this collection but I am not.
console.log(myCollection);
});
simply you can remove the properties date and Author and return the slides
parse: function (response) {
//console.log('response', response[0]['slides']);
delete response[0]['slides'].date;
delete response[0]['slides'].Author;
var temp = [];
$.each( response[0]['slides'], function(index, val) {
temp.push(val);
});
return temp;
}
DEMO
The parse call for your collection should return an array. Inside your for-in loop, add each of the objects you want to an array and return the array from parse when you're done.
var obj = response[0].slides,
returnList = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && prop !== 'date' && prop !== 'Author') {
returnList.push(obj[prop]);
}
}
return returnList;
here's a jsfiddle to demonstrate.
EDIT: here's another jsfiddle to demonstrate using _.map() instead of a for-in loop.
var obj = response[0].slides;
delete obj.date;
delete obj.Author;
return _.map(obj, function (value, key) {
return obj[key];
});

how to parse json string contains circular reference in javascript?

I have the following json string in javascript. This string contains a circular references. I want to parse this string in such a way that the reference will be replaced by its actual object. I use Json.Parse but it creates the json object with references. Is there any way by whihc i can achieve this ?
{
"$id": "1",
"$values": [
{
"$id": "2",
"Event": {
"$id": "3",
"Invitaions": {
"$id": "4",
"$values": [
{
"$ref": "2"
},
{
"$id": "5",
"Event": {
"$ref": "3"
},
"Id": 2,
"Name": "test2",
"Date": "24",
"EventId": 1
}
]
},
"Id": 1,
"Name": "marriage",
"Address": "abcd"
},
"Id": 1,
"Name": "test1",
"Date": "23",
"EventId": 1
},
{
"$ref": "5"
},
{
"$id": "6",
"Event": {
"$id": "7",
"Invitaions": {
"$id": "8",
"$values": [
{
"$ref": "6"
}
]
},
"Id": 2,
"Name": "birthday",
"Address": "abcd"
},
"Id": 3,
"Name": "test3",
"Date": "25",
"EventId": 2
}
]
}
This should do it:
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj)
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i=0; i<refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[refs[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
You should check out Douglas Crockfords JSON-js repo on github: https://github.com/douglascrockford/JSON-js
There's a cycle.js in there that helps you do exactly what you're looking for.
Look at my post here, I've found some bugs in the code above and there wasn't arrays support, check out my improved version: Resolve circular references from JSON object

Categories