如何使用多个条件过滤JavaScript对象

时间:2019-06-06 08:26:04

标签: javascript arrays json

我有一个包含食谱的JSON文件。 现在我的主页上有一个搜索栏,用户可以在其中选中和取消选中复选框,以选择要搜索的食谱属性(名称,成分,标签,类别)。他还可以检查多个条件。

现在,我想根据所选条件过滤对象。 我知道是否只有例如选中我可以使用的“ 名称

recipes.filter(e -> e.name.indexOf(searchString) >= 0)

但是我该如何动态地说“如果还要对成分进行过滤,以找到名称为OR的结果”。

希望您能理解。谢谢。

4 个答案:

答案 0 :(得分:1)

您可以将所有属性放入数组中,然后使用.some()检查是否有任何属性匹配。

const recipes = [
  { name: "pizza", ingredients: "dough tomato mince", category: "meal" },
  { name: "pie", ingredients: "dough sugar", category: "dessert" },
  { name: "stew", ingredients: "potato mince onoin", category: "meal" },
  { name: "donut", ingredients: "sugar", category: "dessert" }
];
// Get these from the checkboxes:
const selected_attributes = [
  "name",
  "ingredients"
];
// Get this from the searchbar:
const seachbar_query = "do";
// Filter all recipes where the name or ingredients contains "do".
// We expect "pizza" and "pie', which contain dough in their ingredients.
// And we also expect "donuts", whose name starts with "do"
const filtered = recipes.filter( recipe => {
  return selected_attributes.some( attribute => {
    return recipe[ attribute ].includes( seachbar_query );
  });
});

console.log( filtered );

答案 1 :(得分:0)

编写一个函数。

recipes.filter(e => {
  if (whatever) return true;
  else {
    // additional checks.
  }
});

就您而言,我猜是这样的:

recipes.filter(e => {
  if (ingredientsAreChecked()) {
    return e.name.matchesSearchString() || e.ingredients.some(i => i.matchesSearchString());
  }
  else {
    return e.name.matchesSearchString();
  }
});

或者如果ingredientsAreChecked()不是计算上很轻松的事情,请执行以下操作:

if (ingredientsAreChecked()) return recipes.filter(e => ...);
else return recipes.filter(e => ...);

答案 2 :(得分:0)

filter是一个高阶函数,它采用一个谓词函数,根据当前项应保留还是不返回truefalse。在您的情况下,该功能是单一代码,但可以传递任何类型的功能。

因此,如果您有复杂的逻辑,建议您将其移到一个命名函数中,在该函数中可以检查多个条件,最后返回一个布尔值:

function isRecipeValid(recipe) {
  if (recipe.something) return false;
  // any arbitrary conditional checking here
  return recipe.conditionOne && recipe.conditionTwo;
}

recipes.filter(isRecipeValid);

答案 3 :(得分:0)

因此,您有两件事:搜索词字段进行搜索。

您可以构建一个过滤函数,该函数接受这两个记录和一个记录(单个配方)并返回true或false。

假设您的复选框为namedescriptioningreedients

您要做的是向过滤器函数发送项目名称,还向您发送要搜索的字段名称。然后在其中插入值。

您可能会遇到这样的事情:

// Disclaimer: these recipes are made up
const recipes = [{
    name: 'lemon cake',
    description: 'a delicious cake',
    ingreedients: ['lemon', 'flour', 'sugar'],
  },
  {
    name: 'sour lemon cake',
    description: 'a delicious cake',
    ingreedients: ['lemon', 'flour', 'not sugar'],
  },
  {
    name: 'choco brownie',
    description: 'a sweet chocolate desert',
    ingreedients: ['chocolate', 'milk', 'flour', 'salt', 'sugar'],
  },
  {
    name: 'vanilla croissant',
    description: 'a yummy pastry with vanilla flavour',
    ingreedients: ['vanilla', 'milk', 'flour'],
  }
];

// To search, we need the search term, and an array of fields by which to search
// We return ANY match, meaning if the search term is in any of the fields, it's a match
function searchAnyField(searchTerm, searchFields) {
  return recipes.filter(item => {
    for (let field of searchFields) {
      if (item[field].indexOf(searchTerm) > -1) {
        return true;
      }
    }
    return false;
  });
}

// the same, but we make sure the search term exists in ALL fields (seems dumb here, but could be a req)
function searchAllFields(searchTerm, searchFields) {
  return recipes.filter(item => {
    // A naive approach is to count in how many fields did we find the term and if it matches the search fields length
    let foundInFields = 0;
    for (let field of searchFields) {
      if (item[field].indexOf(searchTerm) > -1) {
        foundInFields++;
      }
    }
    return foundInFields === searchFields.length;
  });
}

// Let's see it in action
// we'lll just print out the names of found items
console.log('Brownie in name:', printNames(
  searchAnyField('brownie', ['name'])));
console.log('Cake in name:', printNames(
  searchAnyField('cake', ['name'])));
// Let's try multiple fields
console.log('Choc in name and desc:', printNames(
  searchAnyField('choc', ['name', 'description'])));
console.log('flour anywhere:', printNames(
  searchAnyField('flour', ['name', 'description', 'ingreedients'])));
console.log('sweet anywhere:', printNames(
  searchAnyField('sweet', ['name', 'description', 'ingreedients'])));

// How about AND search:
console.log('cake in name AND desc:', printNames(
  searchAllFields('cake', ['name', 'description'])));
console.log('cake everywhere:', printNames(
  searchAllFields('cake', ['name', 'description', 'ingreedients'])));



function printNames(recipes) {
  return recipes.map(r => r.name).join(', ');
}


编辑:您还说过,您有一些嵌套的道具等。以下是更多有关如何解决此问题的示例。

const FIELDS = {
  name: {
    type: 'string',
    path: 'name',
  },
  description: {
    type: 'string',
    path: 'name',
  },
  ingreedients: {
    type: 'array',
    path: 'name',
  },
  price: {
    type: 'nested',
    path: 'details.price',
    nestedType: 'number',
  }
}

// Disclaimer: these recipes are made up
const recipes = [{
    name: 'lemon cake',
    description: 'a delicious cake',
    ingreedients: ['lemon', 'flour', 'sugar'],
    details: {
      price: 45,
    }
  },
  {
    name: 'sour lemon cake',
    description: 'a delicious cake',
    ingreedients: ['lemon', 'flour', 'not sugar'],
    details: {
      price: 45,
    }
  },
  {
    name: 'choco brownie',
    description: 'a sweet chocolate desert',
    ingreedients: ['chocolate', 'milk', 'flour', 'salt', 'sugar'],
    details: {
      price: 42,
    },
  },
  {
    name: 'vanilla croissant',
    description: 'a yummy pastry with vanilla flavour',
    ingreedients: ['vanilla', 'milk', 'flour'],
    details: {
      price: 45,
    },
  }
];

// To search, we need the search term, and an array of fields by which to search
// We return ANY match, meaning if the search term is in any of the fields, it's a match
function searchAnyField(searchTerm, searchFields) {
  return recipes.filter(item => {
    for (let field of searchFields) {
      switch (field.type) {
        case 'string':
          if (item[field.path].indexOf(searchTerm) > -1) {
            return true;
          }
        case 'array':
          if (item[field.path].includes(searchTerm) > -1) {
            return true;
          }
        case 'nested':
          const props = field.path.split('.').reverse();
          let prop = props.pop();
          let val = item[prop];
          while (val && props.length > 0) {
            prop = props.pop();
            val = val[prop]
          }
          if (field.nestedType === 'string') {
            if (val && val.indexOf(searchTerm) > -1) {
              return true;
            }
          } else if (field.nestedType === 'number') {
            return val == searchTerm;
          }
      }
    }
  });
  return false;
}


// Let's see it in action
// we'lll just print out the names of found items
console.log('42 anywhere:', printNames(
  searchAnyField(42, [ FIELDS.price])));
  
console.log('42 anywhere:', printNames(
  searchAnyField(42, [ FIELDS.price, FIELDS.name])));




function printNames(recipes) {
  return recipes.map(r => r.name).join(', ');
}