I ran into an issue where I can't figure out how to pull the data from a TPA API. Would greatly appreciate any help!:)
This is the link to the API: https://www.themealdb.com/api/json/v1/1/random.php
I'm trying to pull out the name of the meal, instructions picture, and the source of the recipe, using an ajax method.
This is the basic structure I have right now:
let randomGenerator = function(){
$.ajax({
method: "GET",
url: "https://www.themealdb.com/api/json/v1/1/random.php",
success: function(r){
console.log(r);
let meals = r["meals"]
let pic = meals[34]
let name = meals[23]
let instructions = meals[29]
let source = meals[52]
$("#random-name").html(name)
$("#random-description").html(instructions)
$("#random-source").html(source)
$("#random-photo").attr("src", pic)
},
error: function(data){
console.log(data);
}
});
}
This is the result from the API when fetched from above link:
{
"meals": [
{
"idMeal": "52964",
"strMeal": "Smoked Haddock Kedgeree",
"strDrinkAlternate": null,
"strCategory": "Breakfast",
"strArea": "Indian",
"strInstructions": "Melt 50g butter in a large saucepan (about 20cm across), add 1 finely chopped medium onion and cook gently over a medium heat for 5 minutes, until softened but not browned.\r\n\r\nStir in 3 split cardamom pods, \u00bc tsp turmeric, 1 small cinnamon stick and 2 bay leaves, then cook for 1 minute.\r\n\r\nTip in 450g basmati rice and stir until it is all well coated in the spicy butter.\r\n\r\nPour in 1 litre chicken or fish stock, add \u00bd teaspoon salt and bring to the boil, stir once to release any rice from the bottom of the pan. Cover with a close-fitting lid, reduce the heat to low and leave to cook very gently for 12 minutes.\r\n\r\nMeanwhile, bring some water to the boil in a large shallow pan. Add 750g un-dyed smoked haddock fillet and simmer for 4 minutes, until the fish is just cooked. Lift it out onto a plate and leave until cool enough to handle.\r\n\r\nHard-boil 3 eggs for 8 minutes.\r\n\r\nFlake the fish, discarding any skin and bones. Drain the eggs, cool slightly, then peel and chop.\u2028\r\n\r\nUncover the rice and remove the bay leaves, cinnamon stick and cardamom pods if you wish to. Gently fork in the fish and the chopped eggs, cover again and return to the heat for 2-3 minutes, or until the fish has heated through.\r\n\r\nGently stir in almost all the 3 tbsp chopped fresh parsley, and season with a little salt and black pepper to taste. Serve scattered with the remaining parsley and garnished with 1 lemon, cut into wedges.",
"strMealThumb": "https:\/\/www.themealdb.com\/images\/media\/meals\/1550441275.jpg",
"strTags": "Brunch,Fish,Fusion",
"strYoutube": "https:\/\/www.youtube.com\/watch?v=QqdzDCWS4gQ",
"strIngredient1": "Butter",
"strIngredient2": "Onion",
"strIngredient3": "Cardamom",
"strIngredient4": "Turmeric",
"strIngredient5": "Cinnamon Stick",
"strIngredient6": "Bay Leaf",
"strIngredient7": "Basmati Rice",
"strIngredient8": "Chicken Stock",
"strIngredient9": "Smoked Haddock",
"strIngredient10": "Eggs",
"strIngredient11": "Parsley",
"strIngredient12": "Lemon",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "50g",
"strMeasure2": "1 chopped",
"strMeasure3": "3 Pods",
"strMeasure4": "1\/4 tsp",
"strMeasure5": "1 small",
"strMeasure6": "Sprigs of fresh",
"strMeasure7": "450g",
"strMeasure8": "1 Litre",
"strMeasure9": "750g",
"strMeasure10": "3",
"strMeasure11": "3 tblsp chopped",
"strMeasure12": "1 chopped",
"strMeasure13": " ",
"strMeasure14": " ",
"strMeasure15": " ",
"strMeasure16": " ",
"strMeasure17": " ",
"strMeasure18": " ",
"strMeasure19": " ",
"strMeasure20": " ",
"strSource": "https:\/\/www.bbcgoodfood.com\/recipes\/2256\/smoked-haddock-kedgeree",
"strImageSource": null,
"strCreativeCommonsConfirmed": null,
"dateModified": null
}
]
}
I keep getting undefined as the array has only one object -> 0, that one random meal, so I'm very confused about how to pull that info out. I created a button that should generate these things on the page, hence the HTML inside.
The return has an array of meals, so just access meals[0].whatever_var_you_want
eg.
$.ajax({
method: "GET",
url: "https://www.themealdb.com/api/json/v1/1/random.php",
success: function(r){
var meal = r.meals[0];
$("#random-name").html(meal.strMeal)
$("#random-description").html(meal.strInstructions);
$("#random-source").html(meal.strSource)
$("#random-photo").attr("src", meal.strMealThumb)
},
error: function(data){
console.log(data);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p id="random-name"></p>
<p id="random-description"></p>
<p id="random-source"></p>
<img id="random-photo"/>
Per www.themealdb.com:
Lookup a single random meal
www.themealdb.com/api/json/v1/1/random.php
Lookup a selection of 10
random meals (only available to $2+ Patreon supporters)
www.themealdb.com/api/json/v1/1/randomselection.php
So it is expected to have only one object in the array. To have multiple random selections you have to subscribe. And probably your code would look a bit like:
$.ajax({
method: "GET",
url: "https://www.themealdb.com/api/json/v1/1/randomselection.php", //needs subscription
success: function(r){
for (let i=0; i<r["meals"].length; i++) { let meal = r["meals"][i].idMeal; let name=r["meals"][i].strMeal; // ... } }
},
error: function(data){
console.log(data);
}
});
You should be able to access the meal using the syntax r["meals"][0].
Once you have the meal object, you can get the image, name, instructions and source using strMealThumb, strName, strInstructions and strSource respectively.
$.ajax({
method: "GET",
url: "https://www.themealdb.com/api/json/v1/1/random.php",
success: function(r) {
let meal = r["meals"][0];
let pic = meal.strMealThumb;
let name = meal.strMeal;
let instructions = meal.strInstructions;
let source = meal.strSource
$("#random-name").html(name)
$("#random-description").html(instructions)
$("#random-source").html(source)
$("#random-photo").attr("src", pic)
},
error: function(data){
console.log(data);
}
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<img id="random-photo" width="80px"/><br>
<b>Name:</b><p id="random-name"></p>
<b>Source:</b><p id="random-source"></p>
<b>Instructions:</b><p id="random-description"></p>
Related
I have a task to return some an object by name of recipe and include a list of it's ingredients and also an object that replaces the instructions with a key value pair of "numSteps": count_of_instruction_steps. I am having a hard time with removing the key of "instructions" for the result.
This is the .json file of recipes:
{
"recipes": [
{
"name": "scrambledEggs",
"ingredients": [
"1 tsp oil",
"2 eggs",
"salt"
],
"instructions": [
"Beat eggs with salt",
"Heat oil in pan",
"Add eggs to pan when hot",
"Gather eggs into curds, remove when cooked",
"Salt to taste and enjoy"
]
},
{
"name": "garlicPasta",
"ingredients": [
"500mL water",
"100g spaghetti",
"25mL olive oil",
"4 cloves garlic",
"Salt"
],
"instructions": [
"Heat garlic in olive oil",
"Boil water in pot",
"Add pasta to boiling water",
"Remove pasta from water and mix with garlic olive oil",
"Salt to taste and enjoy"
]
},
{
"name": "chai",
"ingredients": [
"400mL water",
"100mL milk",
"5g chai masala",
"2 tea bags or 20 g loose tea leaves"
],
"instructions": [
"Heat water until 80 C",
"Add milk, heat until 80 C",
"Add tea leaves/tea bags, chai masala; mix and steep for 3-4 minutes",
"Remove mixture from heat; strain and enjoy"
]
}
]
}
The task is the following:
A GET request to http://localhost:3000/recipes/details/garlicPasta should return, if recipe exists, this .json:
Response body (JSON):
{
"details":
{
"ingredients": [
"500mL water",
"100g spaghetti",
"25mL olive oil",
"4 cloves garlic",
"Salt"
],
"numSteps":5
}
}
Status: 200
And should return this if it does not exist:
Response body (JSON): {}
Status: 200
This is what I am actually getting:
{
name: "garlicPasta",
ingredients: [
"500mL water",
"100g spaghetti",
"25mL olive oil",
"4 cloves garlic",
"Salt"
],
instructions: {
numSteps: 5
}
}
My code is the following:
app.get('/recipes/details/:name', (req, res) => {
let count = 0
const numSteps = {}
const recipe = recipes.find(r => r.name === req.params.name)
const instructions = recipes.map(r => {
if(r.name === req.params.name){
for(let instruction of r.instructions){
count += 1
}
}
})
recipe.instructions = {"numSteps": count}
res.status(200).send(recipe);
})
I wouldn't try to return a modified "recipe" object since the shape of the response you need is different. You can use your existing code to find the correct recipe, and then just create a new object with the response properties you want. Note that you can just use recipe.instructions.length to know how many steps there are.
app.get('/recipes/details/:name', (req, res) => {
let count = 0;
const recipe = recipes.find(r => r.name === req.params.name);
const output = {
details: {
ingredients: recipe.ingredients,
numSteps: recipe.instructions.length
}
};
res.status(200).send(output);
})
You're not creating the details property, and you're adding an instructions property in the result that you don't want.
There's no need to loop to count numSteps, you can just use r.instructions.length. And once you find the recipe with recipes.find(), you don't need another loop to find the recipe name.
You're not checking whether the recipe can be found, so that you return {} in that case.
app.get('/recipes/details/:name', (req, res) => {
const recipe = recipes.find(r => r.name === req.params.name);
let result = {};
if (recipe) {
result.details = {
ingredients: recipe.ingredients,
numSteps: recipe.instructions.length
};
}
res.status(200).send(recipe);
})
This question already has answers here:
How to iterate over a JavaScript object?
(19 answers)
Closed 2 years ago.
I am making a random meal generator that uses the API The mealdb and the results that come back are something like this.
{
"idMeal": "52802",
"strMeal": "Fish pie",
"strDrinkAlternate": null,
"strCategory": "Seafood",
"strArea": "British",
"strInstructions": "01.Put the potatoes in a large pan of cold salted water and bring to the boil. Lower the heat, cover, then simmer gently for 15 minutes until tender. Drain, then return to the pan over a low heat for 30 seconds to drive off any excess water. Mash with 1 tbsp olive oil, then season.\r\n02.Meanwhile put the milk in a large sauté pan, add the fish and bring to the boil. Remove from the heat, cover and stand for 3 minutes. Remove the fish (reserving the milk) and pat dry with kitchen paper, then gently flake into an ovenproof dish, discarding the skin and any bones.\r\n03.Heat the remaining oil in a pan, stir in the flour and cook for 30 seconds. Gradually stir in 200-250ml of the reserved milk (discard the rest). Grate in nutmeg, season, then bubble until thick. Stir in the cream.\r\n04.Preheat the oven to 190°C/fan170°C/gas 5. Grate the artichokes and add to the dish with the leek, prawns and herbs. Stir the lemon zest and juice into the sauce, then pour over. Mix gently with a wooden spoon.\r\n05.Spoon the mash onto the fish mixture, then use a fork to make peaks, which will crisp and brown as it cooks. Sprinkle over the cheese, then bake for 35-40 minutes until golden and bubbling. Serve with wilted greens.",
"strMealThumb": "https://www.themealdb.com/images/media/meals/ysxwuq1487323065.jpg",
"strTags": "Fish,Pie,Breakfast,Baking",
"strYoutube": "https://www.youtube.com/watch?v=2sX4fCgg-UI",
"strIngredient1": "Floury Potatoes",
"strIngredient2": "Olive Oil",
"strIngredient3": "Semi-skimmed Milk",
"strIngredient4": "White Fish Fillets",
"strIngredient5": "Plain flour",
"strIngredient6": "Nutmeg",
"strIngredient7": "Double Cream",
"strIngredient8": "Jerusalem Artichokes",
"strIngredient9": "Leek",
"strIngredient10": "Prawns",
"strIngredient11": "Parsley",
"strIngredient12": "Dill",
"strIngredient13": "Lemon",
"strIngredient14": "Gruyère",
"strIngredient15": "Lemon",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "900g",
"strMeasure2": "2 tbsp",
"strMeasure3": "600ml",
"strMeasure4": "800g",
"strMeasure5": "1 tbsp",
"strMeasure6": "Grating",
"strMeasure7": "3 tbsp",
"strMeasure8": "200g",
"strMeasure9": "1 finely sliced",
"strMeasure10": "200g peeled raw",
"strMeasure11": "Large handful",
"strMeasure12": "Handful",
"strMeasure13": "Grated zest of 1",
"strMeasure14": "25g grated",
"strMeasure15": "Juice of 1",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "",
"dateModified": null
}
I want to be able to iterate through the ingredients not the whole object so i can make them into a list but the ingredients aren't in a single array so i cant iterate only through them.
Is there a way of doing this?
Use Object#entries to get the key7values as new array. Use on this array Array#filter for the keyword at the start of your key.
Object.entries(arr).filter(([key,value]) => key.indexOf('strIngredient')===0);
Here the hole snippet for testing.
let arr = {
"idMeal": "52802",
"strMeal": "Fish pie",
"strDrinkAlternate": null,
"strCategory": "Seafood",
"strArea": "British",
"strInstructions": "01.Put the potatoes in a large pan of cold salted water and bring to the boil. Lower the heat, cover, then simmer gently for 15 minutes until tender. Drain, then return to the pan over a low heat for 30 seconds to drive off any excess water. Mash with 1 tbsp olive oil, then season.\r\n02.Meanwhile put the milk in a large sauté pan, add the fish and bring to the boil. Remove from the heat, cover and stand for 3 minutes. Remove the fish (reserving the milk) and pat dry with kitchen paper, then gently flake into an ovenproof dish, discarding the skin and any bones.\r\n03.Heat the remaining oil in a pan, stir in the flour and cook for 30 seconds. Gradually stir in 200-250ml of the reserved milk (discard the rest). Grate in nutmeg, season, then bubble until thick. Stir in the cream.\r\n04.Preheat the oven to 190°C/fan170°C/gas 5. Grate the artichokes and add to the dish with the leek, prawns and herbs. Stir the lemon zest and juice into the sauce, then pour over. Mix gently with a wooden spoon.\r\n05.Spoon the mash onto the fish mixture, then use a fork to make peaks, which will crisp and brown as it cooks. Sprinkle over the cheese, then bake for 35-40 minutes until golden and bubbling. Serve with wilted greens.",
"strMealThumb": "https://www.themealdb.com/images/media/meals/ysxwuq1487323065.jpg",
"strTags": "Fish,Pie,Breakfast,Baking",
"strYoutube": "https://www.youtube.com/watch?v=2sX4fCgg-UI",
"strIngredient1": "Floury Potatoes",
"strIngredient2": "Olive Oil",
"strIngredient3": "Semi-skimmed Milk",
"strIngredient4": "White Fish Fillets",
"strIngredient5": "Plain flour",
"strIngredient6": "Nutmeg",
"strIngredient7": "Double Cream",
"strIngredient8": "Jerusalem Artichokes",
"strIngredient9": "Leek",
"strIngredient10": "Prawns",
"strIngredient11": "Parsley",
"strIngredient12": "Dill",
"strIngredient13": "Lemon",
"strIngredient14": "Gruyère",
"strIngredient15": "Lemon",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "900g",
"strMeasure2": "2 tbsp",
"strMeasure3": "600ml",
"strMeasure4": "800g",
"strMeasure5": "1 tbsp",
"strMeasure6": "Grating",
"strMeasure7": "3 tbsp",
"strMeasure8": "200g",
"strMeasure9": "1 finely sliced",
"strMeasure10": "200g peeled raw",
"strMeasure11": "Large handful",
"strMeasure12": "Handful",
"strMeasure13": "Grated zest of 1",
"strMeasure14": "25g grated",
"strMeasure15": "Juice of 1",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "",
"dateModified": null
};
let res = Object.entries(arr).filter(([key,value]) => key.indexOf('strIngredient')===0);
console.log(res);
Here is one where I loop through all keys and slice 2 contents from it to match the strIngredient word
yourobject = {
"idMeal": "52802",
"strMeal": "Fish pie",
"strDrinkAlternate": null,
"strCategory": "Seafood",
"strArea": "British",
"strInstructions": "01.Put the potatoes in a large pan of cold salted water and bring to the boil. Lower the heat, cover, then simmer gently for 15 minutes until tender. Drain, then return to the pan over a low heat for 30 seconds to drive off any excess water. Mash with 1 tbsp olive oil, then season.\r\n02.Meanwhile put the milk in a large sauté pan, add the fish and bring to the boil. Remove from the heat, cover and stand for 3 minutes. Remove the fish (reserving the milk) and pat dry with kitchen paper, then gently flake into an ovenproof dish, discarding the skin and any bones.\r\n03.Heat the remaining oil in a pan, stir in the flour and cook for 30 seconds. Gradually stir in 200-250ml of the reserved milk (discard the rest). Grate in nutmeg, season, then bubble until thick. Stir in the cream.\r\n04.Preheat the oven to 190°C/fan170°C/gas 5. Grate the artichokes and add to the dish with the leek, prawns and herbs. Stir the lemon zest and juice into the sauce, then pour over. Mix gently with a wooden spoon.\r\n05.Spoon the mash onto the fish mixture, then use a fork to make peaks, which will crisp and brown as it cooks. Sprinkle over the cheese, then bake for 35-40 minutes until golden and bubbling. Serve with wilted greens.",
"strMealThumb": "https://www.themealdb.com/images/media/meals/ysxwuq1487323065.jpg",
"strTags": "Fish,Pie,Breakfast,Baking",
"strYoutube": "https://www.youtube.com/watch?v=2sX4fCgg-UI",
"strIngredient1": "Floury Potatoes",
"strIngredient2": "Olive Oil",
"strIngredient3": "Semi-skimmed Milk",
"strIngredient4": "White Fish Fillets",
"strIngredient5": "Plain flour",
"strIngredient6": "Nutmeg",
"strIngredient7": "Double Cream",
"strIngredient8": "Jerusalem Artichokes",
"strIngredient9": "Leek",
"strIngredient10": "Prawns",
"strIngredient11": "Parsley",
"strIngredient12": "Dill",
"strIngredient13": "Lemon",
"strIngredient14": "Gruyère",
"strIngredient15": "Lemon",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "900g",
"strMeasure2": "2 tbsp",
"strMeasure3": "600ml",
"strMeasure4": "800g",
"strMeasure5": "1 tbsp",
"strMeasure6": "Grating",
"strMeasure7": "3 tbsp",
"strMeasure8": "200g",
"strMeasure9": "1 finely sliced",
"strMeasure10": "200g peeled raw",
"strMeasure11": "Large handful",
"strMeasure12": "Handful",
"strMeasure13": "Grated zest of 1",
"strMeasure14": "25g grated",
"strMeasure15": "Juice of 1",
"strMeasure16": "",
"strMeasure17": "",
"strMeasure18": "",
"strMeasure19": "",
"strMeasure20": "",
"strSource": "",
"dateModified": null
}
strIngredient = []
for (let key in yourobject) {
if (key.slice(0, -2) == 'strIngredien' || key.slice(0, -2) == 'strIngredient') {
strIngredient.push(yourobject[key])
}
}
console.log(strIngredient)
I started learning node.js and I am facing an error.
Here's the code:
const server = http.createServer((req, res) =>{ //request, response
const pathName = req.url;
if (pathName === '/' || pathName === '/overview'){
res.end('This is the OVERVIEW') // res trimite catre client, req trimite catre server
} else if (pathName === '/product'){
res.end('This is the PRODUCT');
} else if (pathName === '/api') {
fs.readFile(`${__dirname}/dev-data/data.json`, 'utf-8', (err, data) => {
const productData = JSON.parse(data);
response.writeHead(200, { 'Content-type': 'application/json' });
response.end(data);
});
} else{
res.writeHead(404, {
'Content-type': 'text/html',
'my-own-header': 'hello-world'
});
res.end('<h1>This page could not be found!</h1>');
}
res.end('Hello from the server!');
});
the problem is in this if:
else if (pathName === '/api') {
fs.readFile(`${__dirname}/dev-data/data.json`, 'utf-8', (err, data) => {
const productData = JSON.parse(data);
response.writeHead(200, { 'Content-type': 'application/json' });
response.end(data);
});
The error i get:
undefined:1
undefined
^
SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse ()
at ReadFileContext.callback (c:\Users\40721\Desktop\nodeJs&Express\complete-node-bootcamp-master\1-node-farm\index.js:49:38)
at FSReqCallback.readFileAfterOpen [as oncomplete] (fs.js:257:13)
The data.json file i want to read from is this:
[
{
"id": 0,
"productName": "Fresh Avocados",
"image": "🥑",
"from": "Spain",
"nutrients": "Vitamin B, Vitamin K",
"quantity": "4 🥑",
"price": "6.50",
"organic": true,
"description": "A ripe avocado yields to gentle pressure when held in the palm of the hand and squeezed. The fruit is not sweet, but distinctly and subtly flavored, with smooth texture. The avocado is popular in vegetarian cuisine as a substitute for meats in sandwiches and salads because of its high fat content. Generally, avocado is served raw, though some cultivars, including the common 'Hass', can be cooked for a short time without becoming bitter. It is used as the base for the Mexican dip known as guacamole, as well as a spread on corn tortillas or toast, served with spices."
},
{
"id": 1,
"productName": "Goat and Sheep Cheese",
"image": "🧀",
"from": "Portugal",
"nutrients": "Vitamin A, Calcium",
"quantity": "250g",
"price": "5.00",
"organic": false,
"description": "Creamy and distinct in flavor, goat cheese is a dairy product enjoyed around the world. Goat cheese comes in a wide variety of flavors and textures, from soft and spreadable fresh cheese to salty, crumbly aged cheese. Although it’s made using the same coagulation and separation process as cheese made from cow’s milk, goat cheese differs in nutrient content."
},
{
"id": 2,
"productName": "Apollo Broccoli",
"image": "🥦",
"from": "Portugal",
"nutrients": "Vitamin C, Vitamin K",
"quantity": "3 🥦",
"price": "5.50",
"organic": true,
"description": "Broccoli is known to be a hearty and tasty vegetable which is rich in dozens of nutrients. It is said to pack the most nutritional punch of any vegetable. When we think about green vegetables to include in our diet, broccoli is one of the foremost veggies to come to our mind. Broccoli is a cruciferous vegetable and part of the cabbage family, which includes vegetables such as Brussel sprouts and kale. Although the tastes are different, broccoli and these other vegetables are from the same family."
},
{
"id": 3,
"productName": "Baby Carrots",
"image": "🥕",
"from": "France",
"nutrients": "Vitamin A, Vitamin K",
"quantity": "20 🥕",
"price": "3.00",
"organic": true,
"description": "The carrot is a root vegetable that is often claimed to be the perfect health food. It is crunchy, tasty and highly nutritious. Carrots are a particularly good source of beta-carotene, fiber, vitamin K, potassium and antioxidants. Carrots have a number of health benefits. They are a weight loss friendly food and have been linked to lower cholesterol levels and improved eye health."
},
{
"id": 4,
"productName": "Sweet Corncobs",
"image": "🌽",
"from": "Germany",
"nutrients": "Vitamin C, Magnesium",
"quantity": "2 🌽",
"price": "2.00",
"organic": false,
"description": "Also known as maize, corn is one of the most popular cereal grains in the world. Popcorn and sweet corn are commonly eaten varieties, but refined corn products are also widely consumed, frequently as ingredients in foods. These include tortillas, tortilla chips, polenta, cornmeal, corn flour, corn syrup, and corn oil. Whole-grain corn is as healthy as any cereal grain, rich in fiber and many vitamins, minerals, and antioxidants."
}
]
Are you storing the image property as a location to the image? JSON only accepts these datatypes:
a string
a number
an object (JSON object)
an array
a boolean
null
I have a local .json file formated below
{
"results": [
{
"id": 1,
"title": "2 bedroom apartment to rent",
"location": "30 South Colonnade London E14 5EZ",
"description": "The building offers a communal lifestyle which consists of a large lounge area with dining room, working space, TV lounge, gym, stylish meeting rooms, free table tennis, free WIFI and a spacious communal roof terrace. Local transport includes Canning Town DLR and Jubilee Line (0.1 miles). Argo Apartments is managed by Grainger plc, one of the UK's leading professional landlords with over 100 years experience.",
"price": "1,800",
"beds": 2,
"bathrooms": 1,
"landlord": "Hamptons International Lettings",
"images": ["image1", "image2"]
},
{
"id": 2,
"title": "2 bedroom flat to rent",
"location": "Textile Building, Chatham Place, Hackney, London, E9",
"description": "SHORT LET - Forming part of the new eagerly awaited Textile Building in Hackney Central E8, this stunning two double bedroom property has been finished to the highest standard, featuring two modern fitted bathrooms (one en-suite), two equal double bedrooms, spacious open plan reception with brand new integrated kitchen, exposed brickwork and communal underground bike storage.",
"price": "2,400",
"beds": 2,
"bathrooms": 1,
"landlord": "Stirling Ackroyd, Hackney",
"images": ["image1", "image2"]
},
{
"id": 3,
"title": "3 bedroom apartment to rent",
"location": "Sixth Floor Tower Building 11 York Road London SE1 7NX",
"description": "A fantastic three bedroom apartment set in this popular development close to Vauxhall and Stockwell tube stations. The apartment is neutrally decorated throughout and is available either furnished or unfurnished. Residents will enjoy the plethora of shops, bars and restaurants and leisure activities in this popular part of London as well as the excellent commuter links towards Central London.",
"price": "2,050",
"beds": 3,
"bathrooms": 2,
"landlord": "Regent Letting and Property Management",
"images": ["image1", "image2"]
}
],
"newResults" : [
{
"id": 4,
"title": "2 bedroom flat to rent",
"location": "Conway Street, Fitzrovia, W1T",
"description": "Complete is delighted to market this newly renovated period property situated on the corner of Fitzroy Square in the heart of Fitzrovia, W1. The property is located on the ground floor and comes with 2 double bedrooms, shower room, open plan living and kitchen area. The kitchen is fully fitted with Siemens appliances and Duravit fittings in the bathroom. This apartment has high ceiling and retains its original period features. Located on the corner of this garden square this development was built in the 18th Century and benefits from being in the heart of vibrant London whilst also being a quiet residential area.",
"price": "1,500",
"beds": 2,
"bathrooms": 1,
"landlord": "Barnard Marcus Lettings",
"images": ["image1", "image2"]
}
]
}
I want to be able to call the relevent information mapped to a component as below
class ResultsLists extends React.Component {
constructor(props) {
super(props)
this.state = {
results: resultsData
}
}
render() {
const results = this.state.results.results.map((result) =>
<li>{result}</li>
);
return (
<div className="rst-List">
<ul className="rst-List_Items">
{results}
</ul>
</div>
)
}
}
I can't seem to get it too work and am getting the error - Objects are not valid as a React child. There seems to be a varying variety as to why this isn't working, wondered whether someone could sanity check.
You're mapping over the results, but then you're putting the whole result object into the JSX.
This is an example of how to map over data:
const someData = [{ name: "Colin" }, { name: "Ricardo" }];
const App = () => {
const temp = someData.map(o => <li>{o.name}</li>);
return (
<div>
<ul>{temp}</ul>
</div>
);
};
Note that we have {o.name} and not just {o}.
Working example here.
So in your case, you need something like:
const results = this.state.results.results.map((result) =>
<li>{result.title}</li>
);
Currently working on a web app used to search for drink recipes. The idea is to search for a drink and have the name, ingredients and measurements displayed to the user. I am struggling to find an efficient way to iterate through the API response as they do not come back as an array. Below is an example response.
dateModified :"2015-08-18 14:54:32"
idDrink:"11668"
strAlcoholic:"Alcoholic
strCategory:"Ordinary Drink"
strDrink: "Long Island Tea"
strDrinkThumb: "https://www.thecocktaildb.com/images/media/drink/ywxwqs1439906072.jpg"
strGlass: "Highball glass"
strIBA:null
strIngredient1: "Vodka"
strIngredient2:"Light rum"
strIngredient3:"Gin"
strIngredient4:"Tequila"
strIngredient5: "Lemon"
strIngredient6: "Coca-Cola"
strIngredient7:""
strIngredient8:""
strIngredient9:""
strIngredient10:""
strIngredient11:""
strIngredient12:""
strIngredient13:""
strIngredient14:""
strIngredient15:""
strInstructions:
"Combine all ingredients (except cola) and pour over ice in a highball glass. Add the splash of cola for color. Decorate with a slice of lemon and serve."
strMeasure1:"1/2 oz "
strMeasure2:"1/2 oz "
strMeasure3: "1/2 oz "
strMeasure4: "1/2 oz "
strMeasure5:"Juice of 1/2 "
strMeasure6:"1 splash "
strMeasure7:" "
strMeasure8:" "
strMeasure9:" "
strMeasure10:" "
strMeasure11:" "
strMeasure12:" "
strMeasure13:" "
strMeasure14:" "
strMeasure15:" "
strVideo: null
The goal is to map some of the information to a table. Is there an iterative way to clean this up so that only ingredients with values are returned? Or is the best solution to create a separate file for formatting the ingredients?
Currently, the path of least resistance I can think of is creating the following 15 times: strIngredient1 !="".
Below is the API call:
$('#drinkSearch').click(function(){
var word = document.getElementById("sbar").value;
event.preventDefault();
console.log(word)
$.getJSON("https://www.thecocktaildb.com/api/json/v1/1/search.php?s="+ word, function(Result) {
console.log(Result)
Result.drinks.forEach(function(ingredients){
var ing1 = ingredients.strIngredient1;
console.log(ing1);
})
});
});
The API returns an object for each drink with keys like strIngredient1 through strIngredient15, strMeasure1 through strMeasure15, etc. — indeed badly designed.
You can gather all these in an array. There are two different approaches of handling empty values. You can either simply filter empty values or match measures to their ingredients:
Simply filter empty values
These approaches just remove empty values from each to-be-built array. This can lead to inconsistencies since the strMeasure keys actually depend on the strIngredient keys, positionally. Look for the matching approach below to fix that.
Another issue is that the ingredients and measures may be out-of-order sometimes. The matching approach doesn’t have this issue.
Result.drinks.forEach((drink) => {
const drinkEntries = Object.entries(drink),
ingredientsArray = drinkEntries
.filter(([key, value]) => key.startsWith("strIngredient") && value && value.trim())
.map(([key, value]) => value),
measuresArray = drinkEntries
.filter(([key, value]) => key.startsWith("strMeasure") && value && value.trim())
.map(([key, value]) => value);
console.log("Ingredients:", ingredientsArray);
console.log("Measures:", measuresArray);
});
In the filter, key.startsWith("strIngredient") ensures that you get the right fifteen keys, and && value && value.trim() ensures that the value is neither null, nor empty, nor just whitespace (hence trim). All three variations are used randomly.
A less redundant form could look like this:
Result.drinks.forEach((drink) => {
const drinkEntries = Object.entries(drink),
[
ingredientsArray,
measuresArray
] = [
"strIngredient",
"strMeasure"
].map((keyName) => drinkEntries
.filter(([key, value]) => key.startsWith(keyName) && value && value.trim())
.map(([key, value]) => value));
console.log("Ingredients:", ingredientsArray);
console.log("Measures:", measuresArray);
});
Match measures to their ingredients
This approach first builds two arrays for strIngredients and strMeasures. The numeric keys are extracted with parseInt(key.slice(keyName.length)). Object.assigning several {key: value} objects onto an array, where keys are numeric, means building an array with those numeric keys and those values.1
Then the values are filtered such that they remain if any value with the same index is non-empty.
Result.drinks.forEach((drink) => {
const drinkEntries = Object.entries(drink),
// This part build arrays out of the two sets of keys
[
ingredientsArray,
measuresArray
] = [
"strIngredient",
"strMeasure"
].map((keyName) => Object.assign([], ...drinkEntries
.filter(([key, value]) => key.startsWith(keyName))
.map(([key, value]) => ({[parseInt(key.slice(keyName.length))]: value})))),
// This part filters empty values based on the ingredients
{
finalIngredients,
finalMeasures
} = ingredientsArray.reduce((results, value, index) => {
if(value && value.trim() || measuresArray[index] && measuresArray[index].trim()){
results.finalIngredients.push(value);
results.finalMeasures.push(measuresArray[index]);
}
return results;
}, {
finalIngredients: [],
finalMeasures: []
}),
// Optional: zip both arrays
ingredientsWithMeasures = finalIngredients
.map((value, index) => [finalMeasures[index], value]);
// Output
console.log("Ingredients:", finalIngredients);
console.log("Measures:", finalMeasures);
console.log("All ingredients and measures:\n", ingredientsWithMeasures
.map(([measure, ingredient]) => `${(measure || "").trim()} ${(ingredient || "").trim()}`)
.join("\n"));
});
1: Building an array from objects often also works with Array.from, but it requires a length property as well. Instead of calculating that, I just went ahead and used Object.assign instead.
An alternative solution can be:
result.drinks.forEach(function(ingredients){
var ing = Object.keys(ingredients).reduce(function(a, ele) {
if (ele.startsWith('strIngredient') && ingredients[ele].trim() != '') {
if (!a[ingredients.strDrink]) a[ingredients.strDrink] = [];
a[ingredients.strDrink].push(ingredients[ele]);
}
return a;
}, {});
console.log(Object.keys(ing)[0] + ': ' + ing[Object.keys(ing)[0]].join(', '));
})
var result= {
"drinks": [{
"idDrink": "12734",
"strDrink": "Chocolate Drink",
"strVideo": null,
"strCategory": "Cocoa",
"strIBA": null,
"strAlcoholic": "Non alcoholic",
"strGlass": "Coffee mug",
"strInstructions": "Melt the bar in a small amount of boiling water. Add milk. Cook over low heat, whipping gently (with a whisk, i would assume) until heated well. Don't let it boil! Serve in coffee mug.",
"strDrinkThumb": "https:\/\/www.thecocktaildb.com\/images\/media\/drink\/q7w4xu1487603180.jpg",
"strIngredient1": "Chocolate",
"strIngredient2": "Milk",
"strIngredient3": "Water",
"strIngredient4": "",
"strIngredient5": "",
"strIngredient6": "",
"strIngredient7": "",
"strIngredient8": "",
"strIngredient9": "",
"strIngredient10": "",
"strIngredient11": "",
"strIngredient12": "",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strMeasure1": "125 gr",
"strMeasure2": "3\/4 L ",
"strMeasure3": "",
"strMeasure4": "",
"strMeasure5": "",
"strMeasure6": "",
"strMeasure7": "",
"strMeasure8": "",
"strMeasure9": "",
"strMeasure10": "",
"strMeasure11": "",
"strMeasure12": "",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"dateModified": "2017-02-20 15:06:20"
}, {
"idDrink": "12736",
"strDrink": "Drinking Chocolate",
"strVideo": null,
"strCategory": "Cocoa",
"strIBA": null,
"strAlcoholic": "Non alcoholic",
"strGlass": "Coffee mug",
"strInstructions": "Heat the cream and milk with the cinnamon and vanilla bean very slowly for 15-20 minutes. (If you don't have any beans add 1-2 tsp of vanilla after heating). Remove the bean and cinnamon. Add the chocolate. Mix until fully melted. Serve topped with some very dense fresh whipped cream. Serves 1-2 depending upon how much of a glutton you are. For a richer chocolate, use 4 oz of milk, 4 oz of cream, 4 oz of chocolate. Serve in coffee mug.",
"strDrinkThumb": "https:\/\/www.thecocktaildb.com\/images\/media\/drink\/u6jrdf1487603173.jpg",
"strIngredient1": "Heavy cream",
"strIngredient2": "Milk",
"strIngredient3": "Cinnamon",
"strIngredient4": "Vanilla",
"strIngredient5": "Chocolate",
"strIngredient6": "Whipped cream",
"strIngredient7": "",
"strIngredient8": "",
"strIngredient9": "",
"strIngredient10": "",
"strIngredient11": "",
"strIngredient12": "",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strMeasure1": "2 oz ",
"strMeasure2": "6-8 oz ",
"strMeasure3": "1 stick ",
"strMeasure4": "1 ",
"strMeasure5": "2 oz finely chopped dark ",
"strMeasure6": "Fresh ",
"strMeasure7": " ",
"strMeasure8": " ",
"strMeasure9": " ",
"strMeasure10": " ",
"strMeasure11": " ",
"strMeasure12": "",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"dateModified": "2017-02-20 15:06:13"
}, {
"idDrink": "12690",
"strDrink": "Lassi - A South Indian Drink",
"strVideo": null,
"strCategory": "Other\/Unknown",
"strIBA": null,
"strAlcoholic": "Non alcoholic",
"strGlass": "Highball Glass",
"strInstructions": "Blend in a blender for 3 seconds. Lassi is one of the easiest things to make, and there are many ways of making it. Basically, it is buttermilk (yoghurt whisked with water), and you can choose almost any consistency that you like, from the thinnest to the thickest. Serve cold.",
"strDrinkThumb": "https:\/\/www.thecocktaildb.com\/images\/media\/drink\/iq6scx1487603980.jpg",
"strIngredient1": "Yoghurt",
"strIngredient2": "Water",
"strIngredient3": "Cumin seed",
"strIngredient4": "Salt",
"strIngredient5": "Mint",
"strIngredient6": "",
"strIngredient7": "",
"strIngredient8": "",
"strIngredient9": "",
"strIngredient10": "",
"strIngredient11": "",
"strIngredient12": "",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strMeasure1": "1\/2 cup plain ",
"strMeasure2": "1 1\/4 cup cold ",
"strMeasure3": "1\/2 tsp ground roasted ",
"strMeasure4": "1\/4 tsp ",
"strMeasure5": "1\/4 tsp dried ",
"strMeasure6": " ",
"strMeasure7": " ",
"strMeasure8": " ",
"strMeasure9": " ",
"strMeasure10": " ",
"strMeasure11": "",
"strMeasure12": "",
"strMeasure13": "",
"strMeasure14": "",
"strMeasure15": "",
"dateModified": "2017-02-20 15:19:40"
}]
};
result.drinks.forEach(function(ingredients){
var ing = Object.keys(ingredients).reduce(function(a, ele) {
if (ele.startsWith('strIngredient') && ingredients[ele].trim() != '') {
if (!a[ingredients.strDrink]) a[ingredients.strDrink] = [];
a[ingredients.strDrink].push(ingredients[ele]);
}
return a;
}, {});
console.log(Object.keys(ing)[0] + ': ' + ing[Object.keys(ing)[0]].join(', '));
})
I like Xufox's answer, here's another possibility to tame that API, with a hardcoded call for a manhattan :) The idea here is to marshal various things into a final recipe that contains the useful data.
NB I refactored this to show how you could (and maybe should) extract your mapping into a separate function.
const word = 'manhattan';
const url = `https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${word}`;
$.getJSON(url, (result) => {
const recipes = result.drinks.map(extractRecipe);
console.log(recipes);
})
// do your mapping in a defined function.
function extractRecipe(drink) {
const recipe = {
name: drink.strDrink,
glass: drink.strGlass,
instructions: drink.strInstructions,
thumbnail: drink.strDrinkThumb
};
Object.keys(drink)
.filter(key => key.substring(0, 13) === 'strIngredient')
.filter(key => drink[key].trim())
.reduce((ingredients, ingredient) => ingredients.concat(ingredient), [])
.forEach((key, index) => {
let ingIndex = index + 1;
recipe[drink[key]] = drink[`strMeasure${ingIndex}`];
})
return recipe;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>