基于一个单独的搜索数组,使用分层的.reduce()或.filter()函数,在对象数组中查找对象?

3
我有一个由构造函数创建的对象数组。这些对象有比这个例子更多的键值对。但是它们都很好地运作,所以不相关。让我们尝试简洁明了:
示例数组:
let contactsArr = [
  {id: 1, firstName: "Lucas", email: "lucas@fake.com"},
  {id: 2, firstName: "Adrian", email: "adrian@fake.com"},
  {id: 3, firstName: "Betrand", email: "zorro@fake.com"}
  }
];

在 HTML 中,我有一个搜索字段 #search。你猜对了。这个字段旨在搜索联系对象数组中的任何匹配值。
该字段的内容被修剪并复制到一个字符串数组中,由一个或多个空格分隔。
const $input = $("#search").val().toLowerCase().trim().split(/[\s]+/);

没问题。接下来,我想找到并返回contactsArr内与$input中的字符串相等(或包含部分)的对象的任何值。首先版本,我想出了这段代码:
const filteredArr = contactsArr.filter(contact => {
            return contact.firstName.toLowerCase().includes($input) ||
                contact.email.toLowerCase().includes($input) ||
                ... // and more key-value pairs to check
        });

$input 返回字符串或只有一个字符串的数组时,这很有效。如果数组包含多个字符串,则仅搜索并返回第一个字符串的结果。但这也有点混乱,因为对象在将来可能会有更多的键值对。因此,版本2如下所示:
const filteredArr = contactsArr.filter(contact => {
            return Object.values(contact).some(x => {
                if (typeof x === "number") x = x.toString();
                return x.toLowerCase().includes($input);
            });
        });

版本2返回的结果与版本1完全相同,只是它适用于比代码中列出的更多的键值对。太好了!但是当$input数组有多个值时,第二个值仍然被忽略。经过许多尝试和错误,我希望有人能指出我的错误。
这是版本3:(甚至可能是33):)
const filteredArr = contactsArr.filter(contact => {
            return Object.values(contact).some(x => {
                // contact.id number to string
                if (typeof x === "number") x = x.toString();
                // match with lowercase (same as $input)
                x = x.toLocaleLowerCase();
                // check if includes and return true or false
                return $input.some(word => x.includes(word));
            });
        });

预期结果:目标是拥有与 $input 中任何字符串匹配的所有联系人。
非常感谢您提供的任何提示和见解!

你能解释一下什么是“匹配(部分)字符串”吗?提供一些例子会更好。 - Phil
感谢 @CertainPerformance。我跳过得太快了。已更正。 - TVBZ
@Phil,我已经更正了描述。希望现在更清楚了? - TVBZ
可能是重复的问题:Javascript:如何根据属性过滤对象数组? - evolutionxbox
1个回答

2
过去我所做的是通过将所有值连接在一起创建一个大的“索引”字符串。然后,您可以在$input数组上使用Array.prototype.some()。"最初的回答"

let contactsArr = [
  {id: 1, firstName: "Lucas", email: "lucas@fake.com"},
  {id: 2, firstName: "Adrian", email: "adrian@fake.com"},
  {id: 3, firstName: "Betrand", email: "zorro@fake.com"}
];
let searchVal = 'lu rian'
const $input = searchVal.toLowerCase().trim().split(/\s+/);

const filteredArr = contactsArr.filter(contact => {
  // create an index string by joining all the values
  const indexString = Object.values(contact).join(' ').toLowerCase()
  
  // now you can perform a single search operation
  return $input.some(word => indexString.includes(word))
})

console.info(filteredArr)


哈哈,我明白了。那是一个非常有趣的方法,真正的启发。:) 谢谢!不过,我仍然想了解如何使用.filter()或.reduce()来实现它。 - TVBZ
@TVBZ 我现在添加了一小段代码(忘记将 indexString 转换为小写)。我不确定你的评论是什么意思,因为我肯定使用了 filter() - Phil
我尝试了你的解决方案,它很有道理。但出于某种原因,我仍然得到与之前相同的结果。唉,我有点迷失了。你能看一下我的 codepen 吗?搜索功能从规则 260 开始。当搜索 Olson silas 时,我希望返回前两个联系人。非常感谢! - TVBZ
1
@TVBZ 问题出在你的其他逻辑上。只有当 $input.length1 时,你才会对筛选后的数组进行任何操作。修复你的 if...else if... 逻辑,移除第一个 else - Phil
啊啊啊.. 是的!我在专注于排序逻辑时,问题实际上出现在返回逻辑中。:) 非常感谢@Phil。点赞! - TVBZ

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