Last Key in JavaScript array appearing as Length? - javascript

Bit of a weird one. Am using the following code build an array from a json object to make it easier to reference later in the code. However it would appear that when the last item of each array is created, rather than adding a new item, the Key of the item appears as the length of the array.
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null) {
if (obj.Extras != null) {
for (var perf_no in obj.Extras) {
if (extras[perf_no] == undefined) {
var arr = new Array();
for (var extra in obj.Extras[perf_no]) {
if (arr[extra] == undefined) {
arr[extra] = obj.Extras[perf_no][extra];
}
}
extras[perf_no] = arr;
}
}
break;
}
}
}
The resulting array appears as below:
Any ideas what's going on here?
Edit:
Sample of Json below
{"Extras":{"32516":{"24186":"Example text"},"32515":{"24186":"Example text"},"32514":{"24186":"Example text"},"32512":{"24186":"Example text"},"32513":{"24186":"Example text"},"32511":{"24186":"Example text"},"32510":{"24186":"Example text"},"32509":{"24186":"Example text"},"32507":{"24186":"Example text"},"32503":{"24186":"Example text"},"32506":{"24186":"Example text"},"32505":{"24186":"Example text"},"32508":{"24186":"Example text"},"32502":{},"32497":{}}}

What's going on hear is that you are using for..in to iterate over an array, which is a no-no because it iterates properties that are not the array elements (such as the .length property). Instead, use Array#forEach:
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null) {
if (obj.Extras != null) {
obj.Extras.forEach(function (item, idx) {
if (typeof extras[idx] === 'undefined') {
var arr = new Array();
item.forEach(function (item2, idx2) {
if (typeof arr[idx2] === 'undefined') {
arr[idx2] = item2;
}
});
extras[idx] = arr;
}
});
break;
}
}
}
The innermost loop is pretty pointless and can be replaced with Array#slice:
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null) {
if (obj.Extras != null) {
obj.Extras.forEach(function (item, idx) {
if (typeof extras[idx] === 'undefined') {
extras[idx] = item.slice();
}
});
break;
}
}
}
The next inner loop can be replaced with Array#map and two if statements can be combined:
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null&& obj.Extras != null) {
extras = obj.Extras.map(function (item) {
return item.slice();
});
break;
}
}
In fact, most of this code can be simplified:
function findLastElement(arr) {
for (var i = arr.length - 1; i >= 0; i -= 1) {
if (arr[i] != null && arr[i].Extras != null) { return arr[i]; }
}
}
perfsJson = $.parseJSON(result);
var lastElement = findLastElement(perfsJson);
var extras = lastElement
? lastElement.Extras.map(function (item) { return item.slice(); })
: [];

Related

How to get correct array Length in JQuery

I don't know why I getting 2020 array length what process I am missing Please run this code into browser and see result,please give me feedback which I am missing.
var offline_rijksmuseum_child_barcodes_array = new Array();
var offline_rijksmuseum_child_barcodes_new = new Array();
var news = '[{"2018":["testeer","testeer2","testeer3"],"2019":["sd","sd2","sd3"]},{"2018":["dfg"],"2019":["praafd"]}]';
var obj = $.parseJSON(news);
var i = 0;
$.each(obj, function (i, objectData) {
i++;
if(i == 1) {
$.each(objectData, function (key, obj_new) {
if(key == '2018') {
offline_rijksmuseum_child_barcodes_array[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_array);
}
if(key == '2019') {
offline_rijksmuseum_child_barcodes_array[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_array);
}
});
}
else if(i == 2) {
$.each(objectData, function (key, obj_new) {
if(key == '2018') {
offline_rijksmuseum_child_barcodes_new[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_new);
}
if(key == '2019') {
offline_rijksmuseum_child_barcodes_new[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_new);
}
});
}
});
console.log(offline_rijksmuseum_child_barcodes_array.length, offline_rijksmuseum_child_barcodes_array);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Arrays make sense for ordered lists of data (which should not have blank spots, such as with sparse arrays). Your offline_rijksmuseum_child_barcodes_array is a spare array - you're assigning to an index when [index - 1] does not exist in the array - which results in a very odd structure - 2017 <empty> elements followed by two actual elements. You might consider using an object instead of an array:
var offline_rijksmuseum_child_barcodes_array = {};
var offline_rijksmuseum_child_barcodes_new = {};
To get the "length" of the resulting object, you can check the length of its keys:
Object.keys(offline_rijksmuseum_child_barcodes_array).length
That way, lines like
offline_rijksmuseum_child_barcodes_array[key] = obj_new;
will only result in associating the property name [key] with the value obj_new, without also causing spare-array weirdness (like the making the .length of the collection huge in the process).
// var x = ['vdf','dsgfdsfds','dsgfdfgdsfds'];
// console.log(x);
var offline_rijksmuseum_child_barcodes_array = {};
var offline_rijksmuseum_child_barcodes_new = {};
var news = '[{"2018":["testeer","testeer2","testeer3"],"2019":["sd","sd2","sd3"]},{"2018":["dfg"],"2019":["praafd"]}]';
var obj = $.parseJSON(news);
var i = 0;
$.each(obj, function(i, objectData) {
i++;
if (i == 1) {
$.each(objectData, function(key, obj_new) {
if (key == '2018') {
offline_rijksmuseum_child_barcodes_array[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_array);
}
if (key == '2019') {
offline_rijksmuseum_child_barcodes_array[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_array);
}
});
} else if (i == 2) {
$.each(objectData, function(key, obj_new) {
if (key == '2018') {
offline_rijksmuseum_child_barcodes_new[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_new);
}
if (key == '2019') {
offline_rijksmuseum_child_barcodes_new[key] = obj_new;
//console.log(offline_rijksmuseum_child_barcodes_new);
}
});
}
});
console.log(offline_rijksmuseum_child_barcodes_array);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
would suggest to construct a new obj like tmpObj and push key & value and then push tmpObj into array. and you will get the array lenght also.
temp object construction
var tmpObj = {};
tmpObj[key] = tmpObj[key] || [];
tmpObj[key].push(obj_new);
try below code snippet.
var offline_rijksmuseum_child_barcodes_array = new Array();
var offline_rijksmuseum_child_barcodes_new = new Array();
var news = '[{"2018":["testeer","testeer2","testeer3"],"2019":["sd","sd2","sd3"]},{"2018":["dfg"],"2019":["praafd"]}]';
var obj = $.parseJSON(news);
var i = 0;
$.each(obj, function (i, objectData) {
i++;
if(i == 1) {
$.each(objectData, function (key, obj_new) {
var tmpObj = {};
if(key == '2018') {
tmpObj[key] = tmpObj[key] || [];
tmpObj[key].push(obj_new);
offline_rijksmuseum_child_barcodes_array.push(tmpObj );
}
if(key == '2019') {
tmpObj[key] = tmpObj[key] || [];
tmpObj[key].push(obj_new);
offline_rijksmuseum_child_barcodes_array.push(tmpObj);
}
});
}
else if(i == 2) {
$.each(objectData, function (key, obj_new) {
var tmpObj = {};
if(key == '2018') {
tmpObj[key] = tmpObj[key] || [];
tmpObj[key].push(obj_new);
offline_rijksmuseum_child_barcodes_new.push(tmpObj);
}
if(key == '2019') {
tmpObj[key] = tmpObj[key] || [];
tmpObj[key].push(obj_new);
offline_rijksmuseum_child_barcodes_new.push(tmpObj);
}
});
}
});
console.log(offline_rijksmuseum_child_barcodes_array.length, '---', offline_rijksmuseum_child_barcodes_array);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Push to an array in a nested loop is repeating the last value

I am trying to push elements to an array in a nested loop, but only the last item is getting repeated in the final array, where am I going wrong, very new to javascript asynchronous concept.Below is the function in which I push the items to an array.
$scope.showBeList = function(feed) {
if (feed[srcServ.KEY_CONTENT_TEXT]) {
var content = JSON.parse(feed[srcServ.KEY_CONTENT_TEXT])
if (content) {
$scope.beList = {};
for (var key in content) {
var decorationVal;
//reading value from a lokijs collection
var decoration = dataServ[srcServ.CONST_COLLECTION_DECORATION].find({
'name': key
});
if (decoration && decoration.length) {
decorationVal = decoration[0];
if (decorationVal != null) {
var tempObj = JSON.parse(decorationVal.value);
if (tempObj) {
var header = tempObj[key][key + '_HEADER'];
if (header) {
var counter = content[key].length;
var tempItems = [];
for (var j = 0; j < content[key].length; j++) {
(function(j) {
var obj = {};
obj[srcServ.KEY_MAIN_HEADER] = tempObj[key][srcServ.KEY_DESC];
obj[srcServ.KEY_SUB_HEADER] = header[srcServ.KEY_DESC];
obj.id = j;
var itemVal = content[key][j][key + '_HEADER'];
var details = [];
var notes = [];
for (var item in itemVal) {
var val = null;
var found = false;
for (var i = 0; i < header.field.length; i++) {
if (header.field[i].name == item) {
val = header.field[i];
found = true;
break;
}
}
if (found && val != null) {
val[srcServ.KEY_DESC_VALUE] = itemVal[item];
details.push(val);
}
}
obj.details = details;
counter--;
if (counter == 0) {
$scope.showMoreDetails = true;
$scope.beList.beItems = tempItems;
console.log(JSON.stringify($scope.beList));
}
tempItems.push(obj)
})(j);
// $scope.beList.beItems.push(obj);
}
}
}
}
}
}
}
}
}

checking individual element of an array by calling a method

Can i optimize my code bit more to reduce number of line?
I am checking/passing individual array elements? Can i make it re-write in a generic way?
for (var i = 0; i < $scope.studentReport.Students.length; i++)
{
if (_isValueNan($$scope.studentReport.Students[i].Age))
$$scope.studentReport.Students[i].Age = null;
if (_isValueNan($$scope.studentReport.Students[i].Number))
$$scope.studentReport.Students[i].Number = null;
if (_isValueNan($$scope.studentReport.Students[i].Height))
$$scope.studentReport.Students[i].Height = null;
}
var _isValueNan = function (item) {
var result = false;
if (typeof item == 'number' && isNaN(item))
result = true;
return result;
}
With ref to Stumblor's answer:
for (var i = 0; i < $scope.studentReport.Students.length; i++) {
_checkValueNan($$scope.studentReport.Students[i], ["Age", "Number", "Height"]);
}
var _checkValueNan = function (item, values) {
values.forEach(function (val) {
if (typeof item[val] === 'number' && isNaN(item[val])) item[val] = null;
});
}
You can nullify the property internally in the function, and also pass the item and property value in independently. For example:
for (var i = 0; i < $scope.studentReport.Students.length; i++)
{
_checkValueNan($$scope.studentReport.Students[i], "Age");
_checkValueNan($$scope.studentReport.Students[i], "Number");
_checkValueNan($$scope.studentReport.Students[i], "Height");
}
var _checkValueNan = function (item, valueName) {
if (typeof item[valueName] == 'number' && isNaN(item[valueName]))
item[valueName] = null;
}
EDIT:
Leading on from Vicky Gonsalves answer, you could additionally check ANY of the properties of the object, which might be a more scalable solution.
var _checkAllValueNan = function (item) {
for(var key in item) { // iterates all item properties
if (!item.hasOwnProperty(key)) continue; // ensures not prop of prototype
if (typeof item[key] == 'number' && isNaN(item[key])) item[key] = null;
}
}
for (var i = 0; i < $scope.studentReport.Students.length; i++)
{
_checkAllValueNan($scope.studentReport.Students[i]);
}

convert xml to json in Javascript

I have the following javascript function (which I got from Stack Overflow) which converts XML to JSON:
function xmlToJson(xml) {
try {
var obj = {};
if (xml.nodeType == 1) {
if (xml.attributes.length > 0) {
for (var j = 0; j < xml.attributes.length; j++) {
var attribute = xml.attributes.item(j);
obj[attribute.nodeName] = attribute.nodeValue;
}
}
} else if (xml.nodeType == 3) {
obj = xml.nodeValue;
}
if (xml.hasChildNodes()) {
for (var i = 0; i < xml.childNodes.length; i++) {
var item = xml.childNodes.item(i);
var nodeName = item.nodeName;
if (typeof (obj[nodeName]) == "undefined") {
obj[nodeName] = xmlToJson(item);
} else {
if (typeof (obj[nodeName].push) == "undefined") {
var old = obj[nodeName];
obj[nodeName] = [];
obj[nodeName].push(old);
}
obj[nodeName].push(xmlToJson(item));
}
}
}
console.log(JSON.stringify(obj));
return obj;
} catch (e) {
alert(e.message);
}
}
What I want is to return it as an array ([]) when a xml node has at-least single child node and it has a parent node also. In this code it returns map ({}) if xml node has single child node but it is fine with multiple child nodes.
For example, I'd like the XML
<pnode attr1="abc">
<cnode attr2="xyz"></cnode>
</pnode>
to be transformed into the JSON
{
"pnode": {
"attr1": "abc"
},
"cnode": [
{"attr2": "xyz"}
]
}
With the clarification about what you want to achieve, here is an algorithm.
I'll leave my other answer up because I still think the wisest choice is not to play with the structure
function flattenNodes(node, isChild) {
var obj = {}, obj2, i, key, attributes = {};
if (node.attributes && node.attributes.length)
for (i = 0; i < node.attributes.length; ++i)
attributes[node.attributes[i].nodeName] = node.attributes[i].nodeValue;
if (!isChild)
obj[node.nodeName] = attributes;
else {
if (!obj.hasOwnProperty(node.nodeName))
obj[node.nodeName] = [];
else if (!(obj[node.nodeName] instanceof Array))
obj[node.nodeName] = [obj[node.nodeName]];
obj[node.nodeName].push(attributes);
}
attributes = null; // free
if (node.childNodes && node.childNodes.length)
for (i = 0; i < node.childNodes.length; ++i) {
if (node.childNodes[i].nodeType === 3) continue; // skip text node
obj2 = flattenNodes(node.childNodes[i], 1); // recurse
for (key in obj2) // merge
if (obj2.hasOwnProperty(key))
if (!obj.hasOwnProperty(key)) {
obj[key] = obj2[key];
} else {
if (!(obj[key] instanceof Array))
obj[key] = [obj[key]];
obj[key] = obj[key].concat(obj2[key]);
}
}
return obj;
}
Example usage on Node root_node
var root_node;
root_node = new DOMParser().parseFromString(
'<pnode attr1="abc"><cnode attr2="xyz"></cnode></pnode>',
'text/xml'
).documentElement;
var o = flattenNodes(root_node); // create
JSON.stringify(o); // to JSON
// {"pnode":{"attr1":"abc"},"cnode":[{"attr2":"xyz"}]}
If you have XML of the form <foo bar="baz"><foo hello="world"></foo></foo>, the first iteration will cause {foo: {bar: "baz"}}, then the second encounter will modify this to the array form of {foo: [{bar: "baz"}, {hello: "world"}]}
I would form the object representing the XML differently;
Integer nodeType
String nodeName
String nodeValue
Array childNodes
Object attributes
Now you can have the same form independent of number of child nodes/etc
function nodeToObject(node) {
var obj = {}, i;
obj.nodeType = node.nodeType;
obj.nodeName = node.nodeName;
obj.nodeValue = node.nodeValue;
obj.childNodes = [];
obj.attributes = {};
if (node.childNodes && node.childNodes.length)
for (i = 0; i < node.childNodes.length; ++i)
obj.childNodes.push(nodeToObject(node.childNodes[i]));
if (node.attributes && node.attributes.length)
for (i = 0; i < node.attributes.length; ++i)
obj.attributes[node.attributes[i].nodeName] = node.attributes[i].nodeValue;
return obj;
}
And then to transform root_node to JSON,
JSON.stringify(nodeToObject(root_node));
Going in the opposite direction is also possible in JavaScript, with some minor logic based upon nodeType to choose the creation method.

parse xml with jquery - bad xml format

i have this xml file:
<dist>
<key>keynumber1</key>
<string>value1</string>
<key>keynumber2</key>
<string>value2</string>
<key>keynumber3</key>
<string>value3</string>
<key>keynumber4</key>
<integer>value4</integer>
</dist>
how can i parse this with jquery like:
{ "dist": {"keynumber1":"value1", "keynumber2":"value2"}}
Thanks a lot for help
First step is parsing xml with jQuery using $.parseXML(str);
Then I used this pretty function, created by David Welsh
function xmlToJson(xml) {
// Create the return object
var obj = {};
if (xml.nodeType == 1) { // element
// do attributes
if (xml.attributes.length > 0) {
obj["#attributes"] = {};
for (var j = 0; j < xml.attributes.length; j++) {
var attribute = xml.attributes.item(j);
obj["#attributes"][attribute.nodeName] = attribute.nodeValue;
}
}
} else if (xml.nodeType == 3) { // text
obj = xml.nodeValue;
}
// do children
if (xml.hasChildNodes()) {
for(var i = 0; i < xml.childNodes.length; i++) {
var item = xml.childNodes.item(i);
var nodeName = item.nodeName;
if (typeof(obj[nodeName]) == "undefined") {
obj[nodeName] = xmlToJson(item);
} else {
if (typeof(obj[nodeName].length) == "undefined") {
var old = obj[nodeName];
obj[nodeName] = [];
obj[nodeName].push(old);
}
obj[nodeName].push(xmlToJson(item));
}
}
}
return obj;
};

Categories