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

Javascript 遍历设计糟糕的API,其中对象键由文本和数字组成,javascript,jquery,javascript-objects,Javascript,Jquery,Javascript Objects,目前正在开发一个用于搜索饮料配方的web应用程序。其想法是搜索一种饮料,并向用户显示其名称、成分和尺寸。我正在努力寻找一种有效的方法来迭代API响应,因为它们不会以数组的形式返回。下面是一个示例响应 dateModified :"2015-08-18 14:54:32" idDrink:"11668" strAlcoholic:"Alcoholic strCategory:"Ordinary Drink" str

目前正在开发一个用于搜索饮料配方的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);
    })
  });
});

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

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

只需过滤空值 这些方法只是从每个待构建数组中删除空值。这可能会导致不一致,因为
strMeasure
键实际上在位置上依赖于
StringCredit
键。寻找下面匹配的方法来解决这个问题

另一个问题是,成分和措施有时可能会出现问题。匹配方法不存在此问题

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);
});
在中,
key.startsWith(“StringCredit”)
确保获得正确的十五个键,
&&value&&value.trim()
确保值既不是
null,也不是空的,也不仅仅是空白(因此)。这三种变体都是随机使用的

较少冗余的表单可能如下所示:

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
s和
strMeasure
s构建两个数组。使用
parseInt(key.slice(keyName.length))
提取数字键。将多个
{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:从对象构建数组通常也可以使用,但它也需要
length
属性。我没有计算它,而是继续使用
Object.assign

API为每种饮料返回一个对象,其中包含
strIngredient1
strIngredient15
strMeasure1
strMeasure15
等键。 — 确实设计得很糟糕

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

只需过滤空值 这些方法只是从每个待构建数组中删除空值。这可能会导致不一致,因为
strMeasure
键实际上在位置上依赖于
StringCredit
键。寻找下面匹配的方法来解决这个问题

另一个问题是,成分和措施有时可能会出现问题。匹配方法不存在此问题

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);
});
在中,
key.startsWith(“StringCredit”)
确保获得正确的十五个键,
&&value&&value.trim()
确保值既不是
null,也不是空的,也不仅仅是空白(因此)。这三种变体都是随机使用的

较少冗余的表单可能如下所示:

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
s和
strMeasure
s构建两个数组。使用
parseInt(key.slice(keyName.length))
提取数字键。将多个
{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:从对象构建数组通常也可以使用,但它也需要
length
属性。我没有计算它,而是继续使用
对象。赋值。

我喜欢Xufox的答案,这里有另一个驯服API的可能性,通过对
曼哈顿
的硬编码调用:)这里的想法是将各种内容编组到包含有用数据的最终
配方中

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

const word='manhattan';
常量url=`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${word}`;
$.getJSON(url,(结果)=>{
const recipes=result.drinks.map(extractRecipe);
console.log(配方);
})
//在定义的函数中执行映射。
功能提取配方(饮料){
常量配方={
名称:drink.strDrink,
玻璃杯:饮料,
说明:饮料、饮料,
缩略图:drink.strDrinkThumb
};
物体。钥匙(饮料)
.filter(key=>key.substring(0,13)==“StringCredit”)
.filter(key=>drink[key].trim())
.减少((成分,成分)=>成分。浓缩(成分),[])
.forEach((键,索引)=>{
设ingIndex=index+1;
配方[饮料[钥匙]]=饮料[`strMeasure${ingIndex}`];
})
返回配方;
}

我喜欢Xufox的回答,这里有另一种驯服API的可能性,使用硬编码调用f