从一个字符串数组中筛选出一个包含对象的数组。

3
我知道有类似的问题,但是迄今为止没有一个能够帮助我 - 从字符串数组过滤对象数组需要您知道要匹配的键值对,这个问题也同样如此。在这里

假设我有一个对象数组,像这样...

let users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike';
       },
       favouriteFood: 'apples'
    },
    {
       name: 'Steve',
       age: 32,
       pets null
       favouriteFood: 'icecream'
    },
    {
       name: 'Jason',
       age: 31,
       pets: null
       favouriteFood: 'tacos'
    },
    {
       name: 'Jason',
       age: 31,
       pets: {
          cat: 'Jerry'
       },
       favouriteFood: 'bread'
    },
]

现在,我想能够通过匹配任何对象键中的字符串来过滤用户数组。例如,我想过滤掉名字不是 'steve' 的人 - 请记住,我可能还想过滤掉任何不是 42 岁或最喜欢食物不是 'apples' 的人。

filter(term) {
    return objects.filter(x => {
        for(let key of Object.keys(x)) {
          if(typeof(x[key]) === 'object') {
              return JSON.stringify(x[key]).toLowerCase().includes(t);
          } else {
             return x[key].toString().toLowerCase().includes(t);
          }
        }
    });
}

现在这个函数可以工作,但只能针对单个筛选条件。

因此,如果我运行filter('steve'),我将得到

users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike';
       },
       favouriteFood: 'apples'
    },
    {
       name: 'Steve',
       age: 32,
       pets null
       favouriteFood: 'icecream'
    }
]

如果我想要过滤掉最喜欢食用苹果的Steve,该怎么办呢?

我尝试通过更新函数来循环遍历术语数组,并根据数组中所有字符串进行筛选。

所以我已经尝试了:

function filter(terms) {
    return term.forEach((t) => {
      return objects.filter(x => {
        for(let key of Object.keys(x)) {
          if(typeof(x[key]) === 'object') {
              return JSON.stringify(x[key]).toLowerCase().includes(t);
          } else {
             return x[key].toString().toLowerCase().includes(t);
          }
        }
      });
    });

但是当我运行 filter(['steve', 'apples']) 时,结果却是 undefined

我的期望结果应该是

users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike';
       },
       favouriteFood: 'apples'
    }
]

我不太确定我做错了什么或者如何修复这个函数以使其正常工作。
任何帮助将不胜感激。

@MarkMeyer 我不想按名称过滤,我想能够按任何内容进行过滤,我只是使用一个简单的用户对象作为示例。 - Smokey Dawson
1
@MarkMeyer 对于造成的困惑我很抱歉,我会重新措辞问题。我的意思纯粹是举个例子。 - Smokey Dawson
你的问题不是很清楚。你想要匹配仅仅在字符串上(无论是否嵌套)吗?那么对象属性名称呢?例如,你是否想要搜索拥有狗的用户? - Phil
1
@Phil 是的,抱歉,基本上只有字符串值,键名不重要。 - Smokey Dawson
部分匹配怎么办?例如,_"Stev"_ 应该匹配什么? - Phil
显示剩余5条评论
2个回答

5

根据所需值的数组中的每个值是否都包含在给定用户的Object.values中进行筛选:

const filter = arrOfValsNeeded => users.filter(user => {
  const vals = Object.values(user).map(val => typeof val === 'string' ? val.toLowerCase() : val);
  return arrOfValsNeeded.every(needed => vals.includes(needed.toLowerCase()));
});

let users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike'
       },
       favouriteFood: 'apples'
    },
    {
       name: 'Steve',
       age: 32,
       pets: null,
       favouriteFood: 'icecream'
    },
    {
       name: 'Jason',
       age: 31,
       pets: null,
       favouriteFood: 'tacos'
    },
    {
       name: 'Jason',
       age: 31,
       pets: {
          cat: 'Jerry'
       },
       favouriteFood: 'bread'
    },
]

console.log(filter(['steve', 'apples']));

或者,如果您需要递归查找所有基本值:

const allPrimitives = obj => {
  const primitives = [];
  JSON.stringify(obj, (key, val) => {
    if (typeof val !== 'object' || val === null) {
      primitives.push(typeof val === 'string' ? val.toLowerCase() : val);
    }
    return val;
  });
  return primitives;
};
const filter = arrOfValsNeeded => users.filter(user => {
  const vals = allPrimitives(user);
  return arrOfValsNeeded.every(needed => vals.includes(needed.toLowerCase()));
});

let users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike'
       },
       favouriteFood: 'apples'
    },
    {
       name: 'Steve',
       age: 32,
       pets: null,
       favouriteFood: 'icecream'
    },
    {
       name: 'Jason',
       age: 31,
       pets: null,
       favouriteFood: 'tacos'
    },
    {
       name: 'Jason',
       age: 31,
       pets: {
          cat: 'Jerry'
       },
       favouriteFood: 'bread'
    },
]

console.log(filter(['steve', 'apples']));

如果你需要部分匹配,使用vals.some而不是vals.includes,这样你就可以识别子字符串:

const allStrings = obj => {
  const strings = [];
  JSON.stringify(obj, (key, val) => {
    if (typeof val === 'string') {
      strings.push(val.toLowerCase());
    }
    return val;
  });
  return strings;
};
const filter = arrOfValsNeeded => {
  const lowerVals = arrOfValsNeeded.map(str => str.toLowerCase());
  return users.filter(user => {
    const existingStrings = allStrings(user);
    return lowerVals.every(
      lowerNeeded => existingStrings.some(
        existingString => existingString.includes(lowerNeeded)
      )
    );
  });
};

let users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike'
       },
       favouriteFood: 'apples'
    },
    {
       name: 'Steve',
       age: 32,
       pets: null,
       favouriteFood: 'icecream'
    },
    {
       name: 'Jason',
       age: 31,
       pets: null,
       favouriteFood: 'tacos'
    },
    {
       name: 'Jason',
       age: 31,
       pets: {
          cat: 'Jerry'
       },
       favouriteFood: 'bread'
    },
]

console.log(filter(['steve', 'apples']));


抱歉,我的问题表述有点不清楚,基本上我需要它匹配部分单词,所以如果我输入“ste”,“app”,它仍然会返回喜欢苹果的史蒂夫 - 我会更新我的问题。我认为由于我使用了.includes()而不是===,所以这很清楚。抱歉。 - Smokey Dawson
你要查找的值总是字符串吗? - CertainPerformance
@SmokeyDawson如果你继续在前端尝试这样做(特别是对于大数据集),性能将会受到很大影响。你是否考虑过在后端使用某种索引服务? - Phil
@CertainPerformance 我要查找的值始终为字符串,但数据可能包含布尔值、对象和数组 - 但我只想匹配字符串。 - Smokey Dawson
@SmokeyDawson 请注意修改,使用.some代替.includes,这样你就可以为每个值运行一个测试。 - CertainPerformance
@CertainPerformance 谢谢!这正是我在寻找的! - Smokey Dawson

1
抱歉回复晚了。我一直在尝试编写递归代码,以便在几乎任何情况下都能正常工作。最终,我在这里找到了一些非常酷的片段,从中衍生出similar函数,它是从equals函数中派生而来的,稍微考虑了一些兼容性问题。

function similar(a, b){
  if(a === b){
    return true;
  }
  if(a instanceof Date && b instanceof Date){
    return a.getTime() === b.getTime();
  }
  if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')){
    return a === b;
  }
  if (a === null || a === undefined || b === null || b === undefined || a.prototype !== b.prototype){
    return false;
  }
  return Object.keys(b).every(function(k){
    return similar(a[k], b[k]);
  });
}
let users = [
    {
       name: 'Steve',
       age: 42,
       pets: {
           dog: 'spike'
       },
       favouriteFood: 'apples'
    },
    {
       name: 'Steve',
       age: 32,
       pets: null,
       favouriteFood: 'icecream'
    },
    {
       name: 'Jason',
       age: 31,
       pets: null,
       favouriteFood: 'tacos'
    },
    {
       name: 'Jason',
       age: 31,
       pets: {
          cat: 'Jerry'
       },
       favouriteFood: 'bread'
    }
]
var testObj = {name:'Jason', age: 31, pets:{cat:'Jerry'}};
for(var i=0,u,l=users.length; i<l; i++){
  u = users[i];
  if(similar(u, testObj)){
    console.log('contains testObj');
    console.log(u);
  }
  else if(!similar(u, {pets:null}) && !similar(u, {pets:{dog:'spot'}})){
    console.log('not spot');
    console.log(u);
  }
}

similar函数会检查是否有任何非对象的内容完全匹配,或者如果是一个对象,它将检查a是否包含b,考虑到ab在相同深度上具有属性和值,并且b不包含a中不存在的属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接