-2

I want to loop through the JSON file - I believe it is an array of objects. For each object, I want to get the 'name' property as well as the name of each ingredient and its value (in string form) to display in it's own div. JSON file and JS code follow. No matter what I do I cannot seem to loop and access the properties successfully.

JSON

[{
    "name": "Rum & Coke",
    "ingredients": {
      "rum": 50,
      "coke": 150
    }
  },
  {
    "name": "Gin & Tonic",
    "ingredients": {
      "gin": 50,
      "tonic": 150
    }
  },
  {
    "name": "Long Island",
    "ingredients": {
      "gin": 15,
      "rum": 15,
      "vodka": 15,
      "tequila": 15,
      "coke": 100,
      "oj": 30
    }
  },
  {
    "name": "Screwdriver",
    "ingredients": {
      "vodka": 50,
      "oj": 150
    }
  }
]

const data = require('./drinkList.json')
const data = JSON.parse(data)
var mainContainer = document.getElementById("drinks");
for (let i in data) {
  var div = document.createElement("div");
  div.setAttribute("class", "DrinkContainer")

  var drinkName = document.createElement("p")
  drinkName.setAttribute("class", "DrinkName")
  drinkName.innerHTML = data[i]['name']
  div.appendChild(drinkName);

  var drinkIngredients = document.createElement("p")
  drinkIngredients.setAttribute("class", "DrinkIngs")

  const ingredientsInDrink = data[i]['ingredients']
  let ingNameArray = Object.keys(ingredientsInDrink)
  for (const x in ingNameArray) {
    for (const amount in ingredientsInDrink[x]) {
      const ing = x;
      const ingAmount = amount.toString();
      const stringForIngredients = ingAmount + "mls of " + ing
    }
  }
  drinkIngredients.innerHTML = stringForIngredients
  div.appendChild(drinkIngredients)
  mainContainer.append(div)
}
  • Does this answer your question? [How can I access and process nested objects, arrays or JSON?](https://stackoverflow.com/questions/11922383/how-can-i-access-and-process-nested-objects-arrays-or-json) – Ivar Dec 29 '21 at 09:15
  • 1
    I would start with fixing the first error message: `redeclaration of const data` – Andreas Dec 29 '21 at 09:20
  • `const stringForIngredients = ...` - The variable is only available in the inner `for` loop. Using it later in the script (outside of every loop) won't work and will just throw a reference error. – Andreas Dec 29 '21 at 09:20

1 Answers1

0

I've changed your code a little but kept it mostly consistent with what you were trying to achieve.

const data=[{name:'Rum & Coke',ingredients:{rum:50,coke:150}},{name:'Gin & Tonic',ingredients:{gin:50,tonic:150}},{name:'Long Island',ingredients:{gin:15,rum:15,vodka:15,tequila:15,coke:100,oj:30}},{name:'Screwdriver',ingredients:{vodka:50,oj:150}}];

const mainContainer = document.getElementById('drinks');

// For arrays you should be using for...of
for (let obj of data) {

  const div = document.createElement('div');
  div.setAttribute('class', 'drinkContainer')

  const drinkName = document.createElement('p')
  drinkName.setAttribute('class', 'drinkName')

  // We can now use obj.name rather than data[i]['name']
  drinkName.innerHTML = obj.name;
  div.appendChild(drinkName);

  const drinkIngredients = document.createElement('p');
  drinkIngredients.setAttribute('class', 'DrinkIngs');
  
  const entries = Object.entries(obj.ingredients);

  // Instead of a string create a new element
  const ingredients = document.createElement('div');

  // Instead of Object.keys use Object.entries which
  // returns an array of a key/value pair which I've
  // named `name` and `amount`
  for (const [name, amount] of entries) {
   
    // Create a new paragraph element
    const ingredient = document.createElement('p');
    
    // Update the text content of the paragraph
    ingredient.textContent = amount + 'mls of ' + name;

    // And append it to the ingredients div
    ingredients.appendChild(ingredient);
  }
  
  div.appendChild(ingredients);
  
  mainContainer.append(div);

}
.drinkContainer { background-color: #efefef; padding: 1em; margin: 1em; }
.drinkName { font-weight: 700; }
<div id="drinks"></div>

A more modern approach might be to use template strings and map to build up HTML, and then add the final result as the innerHTML of a DOM element.

const data=[{name:"Rum & Coke",ingredients:{rum:50,coke:150}},{name:"Gin & Tonic",ingredients:{gin:50,tonic:150}},{name:"Long Island",ingredients:{gin:15,rum:15,vodka:15,tequila:15,coke:100,oj:30}},{name:"Screwdriver",ingredients:{vodka:50,oj:150}}];

// `map` over the data array
const html = data.map(obj => {

  // Destructure the name and ingredient props
  const { name, ingredients } = obj;
  
  // Create some HTML for the name. We're using
  // template strings for ease of reading
  const nHtml = `<p class="name">${name}</p>`;
  
  // Get the object entries of the ingredient object
  const iEntries = Object.entries(ingredients);
  
  // `map` over the entries and create some HTML
  // for the ingredient name and value. `map` returns
  // and array so we need to `join` it into a string
  const iHtml = iEntries.map(([name, value]) => {
    return `<p>${name} - ${value}`;
  }).join('');
  
  // Return the combined HTML for the name and ingredients
  return `<div class="drink"><p>${nHtml}</p>${iHtml}</div>`;

}).join('');

// Add the HTML to the DOM element
document.querySelector('#main').innerHTML = html;
.name { font-weight: 700; }
.drink { background-color: #efefef; padding: 1em; margin: 1em; }
<div id="main"></div>
Andy
  • 61,948
  • 13
  • 68
  • 95