通过设计糟糕的API进行迭代

时间:2018-04-14 20:39:33

标签: javascript jquery api-design

目前正在开发一个用于搜索饮料配方的网络应用。想法是搜索饮料并向用户显示名称,成分和测量值。我正在努力寻找一种有效的方法来迭代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);

    })
  });

3 个答案:

答案 0 :(得分:3)

API会为每个饮品返回一个对象,其中包含strIngredient1strIngredient15strMeasure1strMeasure15等等等等 - 确实设计得很糟糕。

您可以在阵列中收集所有这些内容。处理空值有两种不同的方法。您可以简单地过滤空值将衡量标准与其成分匹配

只需过滤空值

这些方法只是从每个待构建的数组中删除空值。这可能会导致不一致,因为strMeasure键实际上取决于strIngredient键,位置。请查看下面的匹配方法来解决此问题。

另一个问题是成分和措施有时可能是无序的。 匹配方法没有此问题。

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")确保您获得正确的十五个密钥,而&& 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);
});

将措施与其成分相匹配

此方法首先为strIngredientstrMeasure构建两个数组。数字键用parseInt(key.slice(keyName.length))提取。 Object.assign将几个{key: value}个对象放入数组中,其中key是数字,表示使用这些数字键和那些值构建数组。 1

然后,如果任何值与相同的索引非空,则对这些值进行过滤,以便它们保留。

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代替。

答案 1 :(得分:0)

我喜欢Xufox的回答,这里有另一种驯服API的可能性,用manhattan进行硬编码调用:)这里的想法是将各种事物编组到最后recipe 1}}包含有用的数据。

NB我重构了这一点,以展示你如何(也许应该)将你的映射提取到一个单独的函数中。



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>
&#13;
&#13;
&#13;

答案 2 :(得分: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(', '));

})

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

})
&#13;
&#13;
&#13;