代码之家  ›  专栏  ›  技术社区  ›  eGathergood

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

  •  1
  • eGathergood  · 技术社区  · 6 年前

    目前正在开发一个用于搜索饮料配方的web应用程序。其想法是搜索一种饮料,并将其名称、成分和尺寸显示给用户。我正在努力寻找一种有效的方法来迭代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 回复  |  直到 4 年前
        1
  •  4
  •   Sebastian Simon SamB    6 年前

    API为每种饮料返回一个对象,其键如下 strIngredient1 通过 strIngredient15 ,则, strMeasure1 通过 strMeasure15 确实设计得很糟糕。

    您可以将所有这些集合在一个数组中。处理空值有两种不同的方法。你可以 只需过滤空值 将措施与其成分相匹配 :

    只需过滤空值

    这些方法只是从每个待构建数组中删除空值。这可能会导致不一致,因为 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") 确保您获得正确的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);
    });
    

    将措施与其成分相匹配

    这种方法首先为 StringCredit公司 s和 strMeasure标准 s、 使用提取数字键 parseInt(key.slice(keyName.length)) Object.assign ing多个 {key: value} 数组中的对象,其中 key s是数字,意味着用这些数字键和值构建一个数组。 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 财产也是如此。我没有计算,而是继续使用 对象分配 相反

        2
  •  0
  •   gaetanoM    6 年前

    另一种解决方案可以是:

    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(', '));
    
    })
        3
  •  0
  •   Matt Morgan    6 年前

    我喜欢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>