0

I've tried to write a script for parsing a JSON file stored in the server and returning its pairs of key/values into list items containing the relevant attributes in colon-separated format. I've attempted to do it by using native javascript commands. Although the file is parsed successfully and you can realize that by calling for distinct elements with reference numbers (eg. myObject.pets[1].animal or myObject.pets.length) the loop inside the code that is supposed to capture all items is not working.

Here is the code

<!DOCTYPE html>
<html>
<body>

<ul id="animals"></ul>

<script>
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
  if (this.readyState == 4 && this.status == 200) {
    var myObject = JSON.parse(this.responseText);
    var finalString = "";
    for (i in myObject.pets.length) {
        var currentItem = "<li>" + myObject.pets[i].animal + ": " + myObject.pets[i].name + "</li>";
        var finalString = finalString.concat(currentItem);
    }
    document.getElementById("animals").innerHTML = finalString;
  }
};
xmlhttp.open("GET", "animals.json", true);
xmlhttp.send();
</script>

</body>
</html>

The JSON file

>animals.json
{
    "pets":[
        { "animal":"dog", "name":"Fido" },
        { "animal":"cat", "name":"Felix" },
        { "animal":"hamster", "name":"Lightning" }
    ]
}

and the expected outcome

<li>dog: Fido</li>
<li>cat: Felix</li>
<li>hamster: Lightning</li>
civy
  • 393
  • 2
  • 17

2 Answers2

3

Javascript's for...in functionality loops through the properties of an object, it doesn't increment a variable up to a limit like you expect. Either use it as

  • for (let i in myObject.pets) which will give i the value of each key in the pets object (with indices in an array acting as keys in an object).

  • Or since this is a standard array you can do plenty of things with it. You could loop through it normally with for (let i = 0; i < myObject.pets.length; i++) but based on what you're trying to do I recommend reduce.

I've included a demo which uses reduce to get the finalString in a modern JS way. If you want to stick with your current function though, make sure you don't redefine finalString with var finalString in your loop which you are currently doing. My demo doesn't set the innerHTML of an element and instead writes it to the document, but you can do whatever you want with the finalString.

let apiResult = '{"pets":[{ "animal":"dog", "name":"Fido" }, { "animal":"cat", "name":"Felix" }, { "animal":"hamster", "name":"Lightning" } ] }';

let jsonResult = JSON.parse(apiResult);
    
let finalString = jsonResult.pets.reduce((total, current) => {
  return total.concat("<li>" + current.animal + ": " + current.name + "</li>");
}, "");

document.write(finalString);
Matthew Ludwig
  • 745
  • 6
  • 24
1

I really like @Matthew's answer. Here's a version using map instead of reduce, that makes the code more verbose, but I my opinion also easier to read.

const petsString = '{"pets": [{"animal": "dog", "name": "Fido"}, {"animal": "cat", "name": "Felix"}, {"animal": "hamster", "name": "Lightning"}]}'

const petsArray = JSON.parse(petsString).pets

const petsHtml = petsObject
  .map(pet => `<li>${pet.animal}: ${pet.name}</li>`)
  .join("")

document.write(petsHtml)
Jan Aagaard
  • 10,940
  • 8
  • 45
  • 80
  • Interesting use of map, and it allows him to do more with the result such as joining it with elements in between each list! – Matthew Ludwig Dec 25 '18 at 19:56