Cannot read property 'x' of undefined. second level in json file - javascript

Problem : Can not render second level in the JSON file for some reason but first level works. Second level is giving me undefined error. Please help.
HTML:
<div>
<li data-item="item1">1<p></p><span></span></li>
<li data-item="item2">2<p></p><span></span></li>
<li data-item="item3">3<p></p><span></span></li>
<li data-item="item4">4<p></p><span></span></li>
</div>
JS/JSON
var data = [
{
"word": "hello",
"favnumber": "0070",
"item": "item1",
"color": "red"
},
{
"word": "hello world",
"favnumber": "0233070",
"item": "item2",
"color": "blue",
"Promo": {
"Price": 3.99
}
},
{
"word": "hello mom",
"favnumber": "0070",
"item": "item3",
"color": "pink",
"Promo": {
"Price": 4.99
}
},
{
"word": "hello dad",
"favnumber": "0070",
"item": "item4",
"color": "silver",
"Promo": {
"Price": 8.99
}
}
];
var items = document.querySelectorAll('[data-item]');
for (var e in items) {
var element = items[e];
var name = $(element).attr('data-item');
for (var i in data) {
var item = data[i];
if (name == item.item) {
var colorValue = item.color
var promoPriceValue = item.Promo.Price //this doesn't work//
$(element).find('p').text(colorValue)//this works//
$(element).find('span').text(promoPriceValue)
}
}
}
Example: http://jsfiddle.net/icovermaface/24L02a1q/1/

In addition to not all your objects in data having the exact same data-structure, I would change your for loops from the for ... in pattern to an iterated variable since you are iterating over an array and not enumerating over a javascript object. In other words:
for(var e=0; e < items.length; e++)
and
for(var i=0; i < data.length; i++)
Here is some more info on why not to use the for ... in pattern with arrays: Why is using "for...in" with array iteration a bad idea?

The first item in your array doesnt have a Promo field. This is throwing the undefined error. Do a check to see if the field exists before trying to access it or create a default value
check
if(item.Promo) {
var promoPriceValue = item.Promo.Price
}
default
var promoPriceValue = item.Promo ? item.Promo.Price : 10.99
Also you should change your loop structure, to a for (var i = 0; i < data.length; i++) as Jason mentioned. As structured they throw this error:
Uncaught TypeError: Cannot use 'in' operator to search for 'getAttribute' in 4
working fiddle - https://jsfiddle.net/24L02a1q/7/

Related

Print the same child nodes from an object but two levels deep

I'm trying to create a piece of JavaScript that can read through specific parts of a linked object and place them iteratively into another piece of code which then places the code into HTML and into the front-end.
I've managed to get the fetch part working whereby it pulls in the JSON and can be read in the console, when summoned. Once the code runs, I'm able to refer to the data and bring out the whole dataset with something like:
console.log(AllOffers);
and I can drill down into something like the offerName in the JSON by using the following syntax in a variable and calling it in the console:
var OfferName = data.offersBreakdown.allOffers[0].offers[0].offerName;
However this only pulls in the first iteration of offerName because in the variable I've set it to look into the first iteration of its parent, 'offers'. What I'm looking to do is create a variable which prints all of the offerName data so that I can call on it instead of the data_test variable further down in the code, which processes the data into HTML. Sounds confusing? It is.
Ideally what I think I need is to be able to ask it to look into each child item of 'offers' (rather than just the first one) and then have it look for 'offerName'. I can't work out how one would achieve this. The best I can come up with is to remove the [0] from 'offers', but if I do that, it returns undefined as the result.
Here's my JavaScript (and a bit of jQuery):
<script>
// fetch call for the JSON data (see below)
fetch('api_url', {
headers: {
'Authorization': 'auth_token'
}
})
.then(response => response.json())
.then(function (data) {
var AllOffers = data.offersBreakdown.allOffers[0];
var AllOffers_Offers = data.offersBreakdown.allOffers[0].offers;
var OfferName = data.offersBreakdown.allOffers[0].offers[0].offerName;
var OfferImageUrl = data.offersBreakdown.allOffers[0].offers[0].imageUrl;
console.log(AllOffers);
function createCard(cardData) {
var cardTemplate = [
'<div class="card">',
'<p>My name is: ',
cardData.Offer || 'No offer',
'</p>',
'<p>My job is: ',
cardData.Img || 'No image',
'</p></div>'
];
// a jQuery node
return jQuery(cardTemplate.join(''));
}
var data_test = [
{ "Name": OfferName, "Img": OfferImageUrl },
{ "Name": OfferName, "Img": OfferImageUrl },
{ "Name": OfferName, "Img": OfferImageUrl },
];
var cards = jQuery();
// Store all the card nodes
data_test.forEach(function(item, i) {
cards = cards.add(createCard(item));
});
// Add them to the page... for instance the <body>
jQuery(function() {
jQuery('body').append(cards);
});
</script>
Here's the JSON
<script>
// the JSON
{
"offersBreakdown": {
"totalAddedOffers": 0,
"totalOffers": 2,
"totalAddedRewards": 0,
"totalRewards": 0,
"totalAddedStreakOffers": 0,
"totalStreakOffers": 0,
"allOffers": [
{
"offers": [
{
"offerName": "Offer name 1",
"imageUrl": "https://url_path_1.jpg"
},
{
"offerName": "Offer name 2",
"imageUrl": "https://url_path_2.jpg"
},
{
"offerName": "Offer name 3",
"imageUrl": "https://url_path_3.jpg"
},
{
"offerName": "Offer name 4",
"imageUrl": "https://url_path_4.jpg"
}
]
}
</script>
I'm assuming what you're looking for is a way to loop through all of the offerNames, in which case a simple for loop would suffice. Since your data includes nested arrays and objects, we need two loops, one to iterate through your allOffers array and then a nested for loops to iterate through the offers array inside of your allOffers array
var data = {
"offersBreakdown": {
"totalAddedOffers": 0,
"totalOffers": 2,
"totalAddedRewards": 0,
"totalRewards": 0,
"totalAddedStreakOffers": 0,
"totalStreakOffers": 0,
"allOffers": [{
"offers": [{
"offerName": "Offer name 1",
"imageUrl": "https://url_path_1.jpg"
}, {
"offerName": "Offer name 2",
"imageUrl": "https://url_path_2.jpg"
}, {
"offerName": "Offer name 3",
"imageUrl": "https://url_path_3.jpg"
}, {
"offerName": "Offer name 4",
"imageUrl": "https://url_path_4.jpg"
}]
}]
}
};
var allOffers = [];
var jsonObjectAllOffers = data.offersBreakdown.allOffers;
for (var i = 0; i < jsonObjectAllOffers.length; i++) {
var offers = jsonObjectAllOffers[i].offers;
for (var j = 0; j < offers.length; j++) {
var objectToAppend = {
"Name": offers[j]["offerName"],
"Img": offers[j]["imageUrl"]
};
allOffers.push(objectToAppend);
}
}
console.log(allOffers);
And now you can use your allOffers variable to loop through with the "forEach" and make into HTML

how to add filter for json data

I am having many such entries in my json data. The "type" consists of tw attributes i.e. income and expense. How to print the "label" which have type="expense" in using JavaScript.
This json data below is just an example.
Check the image to get a better idea of json data.
"expenses_veterinary":{
label:"Veterinary, breeding, and medicine"
name:"expenses_veterinary"
total:0
type:"expense"
}
console.log($ctrl.gold_standard_categories); prints all the json data.
I tried the code written below but its not working.
if($ctrl.gold_standard_categories.name.type=expense){
console.log($ctrl.gold_standard_categories.label);
}
It prints all the data because of the single equals sign in your if statement.
It means you are always trying to assign the value "expense" to your type property and so the if statement will always evaluate to true.
What you are intending to do is compare the values, not assign a value.
https://jsfiddle.net/yxL4rpj2/
assume that in var money you store the json response
var grouppedMoney = {
expenses: [],
incomes: []
};
for(var i = 0; i <= money.length - 1; i++){
for(moneyType in money[i]){
var _typeString = money[i][moneyType].type == 'expense' ? 'expenses' : 'incomes';
grouppedMoney[_typeString].push(money[i][moneyType]);
}
}
Here is the basic example.Iterate over your object and see type value is equal to expense then print the label value.
var data = {
"expenses_veterinary":{
"label":"Veterinary, breeding, and medicine",
"name":"expenses_veterinary",
"total":0,
"type":"income"
},
"expenses_car":{
"label":"bus and truck",
"name":"expenses_car",
"total":0,
"type":"expense"
}
};
for (var property in data) {
if (data.hasOwnProperty(property)) {
// do stuff
if(data[property]['type']=='expense'){
console.log(data[property]['label']);
}
}
}
It seems you are using AngularJS ($ctrl) so you may consider using either native angular filters or custom filter to do this.
BTW, using vanilla JS, this simple loop will work :
for(var key in jsonData) {
if("expense" === jsonData[key]["type"]) {
console.log(jsonData[key]["label"]);
}
}
Sample snippet
var jsonData = {
"expensesigoods": {
"name": "expenses_goods",
"label": "Cost of goods sold",
"type": "expense",
"total": 0
},
"expensesicar": {
"name": "expenses_car",
"label": "Car and truck expenses",
"type": "expense",
"total": 0
},
"expensesichemicals": {
"name": "expenses_chemicals",
"label": "Chemicals",
"type": "expense",
"total": 0
},
"expensesiconservation": {
"name": "expenses_conservation",
"label": "Conservation expenses",
"type": "other",
"total": 0
}
}
for(var key in jsonData) {
if("expense" === jsonData[key]["type"]) {
console.log(jsonData[key]["label"]);
}
}
Try Array.filter() method to filter the data based on type="expense".
Working Fiddle
var obj = {
"expenses_goods":{
"label":"expenses_goods",
"name":"expenses goods",
"total":0,
"type":"income"
},
"expenses_cars":{
"label":"expenses_cars",
"name":"expenses cars",
"total":0,
"type":"expense"
},
"expenses_veterinary":{
"label":"expenses_veterinary",
"name":"expenses veterinary",
"total":0,
"type":"income"
}
};
var res = Object.keys(obj).filter(item => { return obj[item].type == 'expense' });
for (var i in res) {
document.getElementById("result").innerHTML = obj[res[i]].label;
}
<div id="result">
</div>

Array Mapping in AngularJs

I have two arrays
$scope.tags = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }]
Other one is
$scope.skillsInterested = [1,2];
What is want to do ?
How can i map the above arrays and print only names of the id's in$scope.skillsInterested
I want to print names in first array only the id's present in second.
I have tried this after getting several answers
var tag_map = {};
for (var x = 0; x < $scope.tags.length; x++) {
tag_map[$scope.tags[x]['id']] = $scope.tags[x]['name'];
}
$scope.skillsInts = $scope.skillsInterested.map(function(x) {
return tag_map[x]
On running console.log
console.log("Result", tag_map);
It sometimes give result sometimes it gives 'map' of undefined.
TypeError: Cannot read property 'map' of undefined
at controllers.js:141
at angular.js:16383
at m.$eval (angular.js:17682)
at m.$digest (angular.js:17495)
at m.$apply (angular.js:17790)
at l (angular.js:11831)
at J (angular.js:12033)
at XMLHttpRequest.t.onload (angular.js:11966)
Thanks in advance.
Make a map of your data that looks like this:
var tagMap = { 1: "python", 2: "NodeJs" /* etc. */ };
You can do this by looping over your tags and adding a new property to an object. reduce lets you do this without creating any extra variables.
Then, you can select names from your newly created object using the [] notation: tagMap[1] returns "pyhton".
var tags = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }]
var selectedExpTags = [1,2];
// Make a map for `id: name`
var tagMap = tags.reduce(function(map, tag) {
map[tag.id] = tag.name;
return map;
}, {});
// Quickly select names from the map:
var selectedNames = selectedExpTags.map(function(id) {
return tagMap[id];
});
console.log(selectedNames);
Using this approach, you minimise the iterations over your data. The creation of the map loops over the tags once. Creating the array with names, loops over the selected tags once. So, roughly, the "loop count" is tags.length + selectedTags.length. If you would use an indexOf based approach, your loop count would be tags.length * selectedTags.length.
Use the filter function for first, and then check the id's existnent then map the names from the array.
var first = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }];
var selectedExpTags = [1,2];
var names = first.filter(item => selectedExpTags.some(id => item.id === id)).map(item => item.name);
console.log(names);
You can loop over $scope.selectedExpTags and get a list of all names. You can use array.find if you want first value only.
Sample
var first = [
{ "id": 1, "name": "python" },
{ "id": 2, "name": "NodeJs" },
{ "id": 3, "name": "git" }];
var selectedExpTags = [1,2];
var names = selectedExpTags.map(x=> first.find( y=> y.id === x ).name )
console.log(names);
$scope.newArray = []; // If you need a new array to work with
angular.forEach($scope.tags, function(tag){
$scope.selectedExpTags.forEach(function(selectedTag){
if(selectedTag == tag.id){
//tag.hide = false; // - If you want to update the current array
$scope.newArray.push(tag);
}
// else{ // - If you want to update the current array
// tag.hide = true;
// }
})
})
Lodash is more efficient than angular for manipulating data.

Replacing all instances of a value in JSON using pure JavaScript

Is it possible using JavaScript only to replace all instances of the version with another number and then return the JSON structure intact?
{
"savedSearches": [{
"id": 123,
"version": 10,
"name": "Project Manager",
"query": "www.foo.com"
}, {
"id": 123,
"version": 10,
"name": "Project Manager",
"query": "www.foo.com"
}],
"deletedSavedSearches": []
}
I need this to be very quick and lightweight as I'll be using it within JMeter.
JSON structure is nothing more than a JavaScript object. You can iterate over its properties and modify their values as usual. For instance, to increase each version by one:
var json = { … }
for (var i in json.savedSearches) json.savedSearches[i].version += 1;
You can try this
var jsonObject = JSON.parse(yourJSONString);
for(var i = 0, len = jsonObject.savedSearches.length; i < len; i++) {
jsonObject.savedSearches[i].version = "Number you want here";
}
If you, for some reason, want to return a string:
JSON.stringify(jsonObject)
If you already have the object, you can skip the JSON.parse

Creating an multidimensional Array from string (hierarchical data)

I have an Array with this kind of values:
val = [ ['L-2-4-1','john','bla1'],
['L-1-1-26','bohn','bla2'],
['L-2-1','cohn','bla3'],
['L-1-1-05','rohn','bla4'],
['L-1-1','gohn','bla5']
['L-2-3-1','zohn','bla-finally'] ];
The number-sequence is always unique and "0" is never used.
What I'm trying to get would be something like this:
ser = [ [undefined],
[ [undefined],[ ['gohn'],['bla5'] ], [undefined], ... , [ ['bohn'], ['blah2'] ] ],
...
];
The purpose is to be able to access the data like this:
ser[2][4][1][0]; // Array('john','bla1')
ser[1][1][0]; // Array('gohn','bla5')
ser[1][1][26][0]; // Array('bohn','bla2')
and also to loop through all elements.. for instance:
for(var i = 0; i <= ser[1][1].length; i++){ //code }
The main problem I have is that I was not able to set the variables the same way I intend to read them. Because this does NOT work, since I need to declare all arrays separately as arrays (right?)
var ser[1][1][26][0] = ['john','bla1']; // Nop;
I don't know the maximum depth of the tree
Trying to build the arrays from "inside out" or from "right to left" -however it is best described- I always end up overwriting previously set array elements.
Maybe the whole idea is too complicated (or at least not ideal) for the purpose? What would you suggest? I have the feeling I´m trying to do the right thing but the wrong way... Something like organizing marbles on a glass surface. Everything keeps moving around...
Have you considered representing your data in JSON?
It allows for complex structures that are otherwise too confusing to keep in your head. It's like XML meets JavaScript arrays. Rather self-describing and easy to follow. You can read the lengths and sizes of objects easily and it's quite fast. You can use values instead of array positions and re-think the structure of your data.
http://json.org/example.html
Here is a record in JSON:
{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
},
http://labs.adobe.com/technologies/spry/samples/data_region/JSONDataSetSample.html
short and sweet:
var i, j, t, final = [];
for (i = 0; i < val.length; i++) {
t = val[i][0].split('-');
for (j = 1; j < 5; j++) {
t[j] = parseInt(t[j], 10) || 0;
}
final[t[1]] = final[t[1]] || [];
final[t[1]][t[2]] = final[t[1]][t[2]] || [];
final[t[1]][t[2]][t[3]] = final[t[1]][t[2]][t[3]] || [];
final[t[1]][t[2]][t[3]][t[4]] = final[t[1]][t[2]][t[3]][t[4]] || [];
final[t[1]][t[2]][t[3]][t[4]].push(val[i].slice(1));
}
final now has the correct data as you specified...
however, you might want to consider using objects instead of arrays (change all [] to {}) as the random insertion points in arrays lead to series of empty (null) values, the only caveat would be that you'd have to use a for (var key in obj) style loop...
hope this helps -ck
IF YOU NEED DYNAMIC DEPTH
var i, j, t, o, depth = 4, final = [];
for (i = 0; i < val.length; i++) {
t = val[i][0].split('-');
o = final;
for (j = 1; j <= depth; j++) {
t[j] = parseInt(t[j], 10) || 0;
o[t[j]] = o[t[j]] || [];
o = o[t[j]];
}
o.push(val[i].slice(1));
}
now depth is the constant at which the data is stored missing or unparsable "keys" or "indices" depending on how you want to think of them, default to 0
enjoy -ck

Categories