迭代设计糟糕的 API,其中对象键由文本和数字组成

问题描述 投票:0回答:4

目前正在开发一个用于搜索饮料配方的网络应用程序。这个想法是搜索一种饮料并向用户显示名称、成分和尺寸。我正在努力寻找一种有效的方法来迭代 API 响应,因为它们不会以数组形式返回。以下是响应示例。

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

目标是将一些信息映射到表格中。是否有一种迭代方法来清理这个问题,以便只返回具有值的成分?或者创建一个单独的文件来格式化成分是最好的解决方案吗?

目前,我能想到的阻力最小的路径是创建以下15次:

strIngredient1 !=""

API调用如下:

$('#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);
    })
  });
});
javascript jquery javascript-objects
4个回答
5
投票

API 为每种饮料返回一个对象,其中包含

strIngredient1
strIngredient15
strMeasure1
strMeasure15
等键。 — 确实设计得很糟糕。

您可以将所有这些收集在一个数组中。有两种不同的处理空值的方法。您可以简单地过滤空值将测量值与其成分进行匹配

简单过滤空值

这些方法只是从每个要构建的数组中删除空值。这可能会导致不一致,因为

strMeasure
键实际上依赖于
strIngredient
键,位置。寻找下面的matching方法来解决这个问题。

还有一个问题是成分和措施有时可能会出现问题。 匹配方法没有这个问题。

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);
});

filter
中,
key.startsWith("strIngredient")
确保您获得正确的 15 个键,而
&& value && value.trim()
确保该值既不是
null
,也不是空,也不是空白(因此是
trim
)。所有三种变体都是随机使用的。

冗余较少的形式可能如下所示:

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);
});

将措施与其成分相匹配

此方法首先为

strIngredient
strMeasure
构建两个数组。数字键用
parseInt(key.slice(keyName.length))
提取。
Object.assign
将多个
{key: value}
对象放入数组中,其中
key
是数字,意味着使用这些数字键和这些值构建一个数组。1

然后对值进行过滤,如果具有 相同索引any 值非空,则它们会保留。

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:从对象构建数组通常也可以使用

Array.from
,但它也需要
length
属性。我没有计算它,而是继续使用
Object.assign
来代替。


0
投票

替代解决方案可以是:

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(', '));

})


0
投票

我喜欢Xufox的答案,这是驯服该API的另一种可能性,通过对

manhattan
的硬编码调用:)这里的想法是将各种东西编组到包含有用数据的最终
recipe
中。

注意,我重构了它,以展示如何(也许应该)将映射提取到单独的函数中。

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>


0
投票

更简单的解决方案 相信您已从鸡尾酒 API 获得成功响应,简单的方法是替换 Drinks[0].strIngredient{num} 末尾的数字,并检查该值是否为 null,如果该值为 null您可以简单地忽略它并打印非空值。我在下面有一个代码片段::

我已经测试过了,这适用于其他参数

<div class="secondFloat">
            <% for(let i=1;i<=15;i++) { %>
                <% let ingredient = content.drinks[0][`strIngredient${i}`] %>
                <% if(ingredient !== null){ %>
                    <h1><%=ingredient%></h1>
                <%} %>
            <%} %>

也在 JSON 中。 希望这有帮助!!!

© www.soinside.com 2019 - 2024. All rights reserved.