如何在JavaScript中检查一个对象是否包含至少一个键的值包含子字符串?

14

我希望编写一个函数,检查一个对象中是否至少有一个值包含子字符串。类似于这样的伪代码:

const userMatchesText = (text, user) => user.includes(text);
我的对象的完整结构(
因此,对于像以下用户一样的用户:
const user = {
    id: '123abc',
    info: {
        age: 12,
        bio: 'This is my bio' 
    },
    social: {
        chatName: 'Chris',
        friends: ['friend1', 'other friend'],
        blocks: ['Creep']
    }
    //Etc. The objects I'm working with contain nested objects and arrays, etc.
}
userMatches('bi', user) 应该返回 true,因为子字符串 'bi' 在个人资料中被发现:'this is my bio'。userMatches('324d, user) 同样应返回false。然而,usermatches('blocks', user) 应该返回false,因为该子字符串只在键之一中找到,而不在任何一个值中。

我正在处理的对象看起来像这样(Mongoose模式):

{
    account  : {
        dateOfCreation : Number
    },
    social   : {
        chatName         : String,
        friends          : [ { type: String } ],
        blocks           : [ { type: String } ],
        sentRequests     : [ { type: String } ],
        recievedRequests : [ { type: String } ],
        threads          : [ { type: String } ]
    },
    info     : {
        age            : Number,
        bio            : String,
        displayName    : String,
        profilePicture : String,
        subjects       : {
            tutor   : [
                {
                    subject : String,
                    level   : String
                }
            ],
            student : [
                {
                    subject : String,
                    level   : String
                }
            ]
        }
    },
    facebook : {
        id         : String,
        firstName  : String,
        middleName : String,
        fullName   : String,
        lastName   : String
    }
}
到目前为止,我发现最好的方法是从对象中解构出所有键都是字符串的部分,然后使用mapincludes,如下面的函数。

到目前为止,我发现最好的方法是从对象中解构出所有键都是字符串的部分,然后使用mapincludes,如下面的函数。

const doesUserMatchText = (user, text) => {
    const { social: { chatName }, info: { displayName }, facebook: { firstName, middleName, lastName } } = user;
    const possibleMatches = [ chatName, displayName, firstName, middleName, lastName ];
    let match = false;
    possibleMatches.map(possibleMatch => {
        if (possibleMatch.includes(text)) {
            return (match = true);
        }
    });
};

然而,这确实很烦人(也可能非常低效),因为我正在处理的对象非常大。如果我可以调用 userMatchesText(text, user) 并获得布尔值就太好了。非常感谢您的帮助!

另外,请注意我没有解构所有字符串键。此函数的目的是根据搜索查询过滤用户,并且我认为让用户通过其生物、ID等搜索其他用户可能没有太多意义,而只应通过其各种“名称”进行搜索。


1
除非这些对象有某种可以利用的结构,否则不可避免地必须检查每个键以查看是否匹配。(排除短路“true”路径,如果我们在搜索中早期找到匹配项) - Kallmanation
谢谢你的答案。我会使用对象的完整结构来更新答案。我找到了一种方法,而且运行得非常好(我想),但正如你所说,我必须单独访问这些键值对。 - Christoffer Corfield Aakre
3个回答

8
您可以使用递归函数来遍历整个对象。只需确保该对象没有任何循环引用即可...

const user = {
    id: '123abc',
    info: {
        age: 12,
        bio: 'This is my bio' 
    },
    social: {
        chatName: 'Chris',
        friends: ['friend1', 'other friend'],
        blocks: ['Creep']
    }
    //Etc. The objects I'm working with contain nested objects and arrays, etc.
};

function userMatchesText(text, user) {
    if (typeof user === "string") return user.includes(text);
    return Object.values(user).some(val => userMatchesText(text, val));
}

console.log(userMatchesText("bi", user));
console.log(userMatchesText("other fri", user));
console.log(userMatchesText("zzz", user));


太棒了,some 的使用非常好! - Sidney
我知道这是一个较旧的答案,但是否有任何方法可以考虑阶段之后的单词。例如,如果userMatchesText("bi bro",user),它会返回false。 - Swink
如果user包含任何nullundefined值,此函数将在Object.values调用时出错。 - Aaron
我正在处理null,这给了我错误,所以我在@CRice的内部包装了一个“truthy”语句来过滤掉所有的null。https://dev59.com/pm035IYBdhLWcg3wSN4G - Pixelsmith

2

纯JavaScript。这个函数会迭代对象的键,一旦找到匹配就返回true。

最坏的情况是结果为false时,它会迭代所有的键和子键。

(function() {
  var user = {
    id: '123abc',
    info: {
      age: 12,
      bio: 'This is my bio'
    },
    social: {
      chatName: 'Chris',
      friends: ['friend1', 'other friend'],
      blocks: ['Creep']
    }
    //Etc. The objects I'm working with contain nested objects and arrays, etc.
  };

  console.log('userMatches(\'bi\', user): ' + userMatches('bio', user));
  console.log('userMatches(\'324d\', user): ' + userMatches('324d', user));
  console.log('usermatches(\'blocks\', user) ' + userMatches('blocks', user));

  function userMatches(str, obj) {
    var queue = [];
    for (var k in obj) {
      if (obj.hasOwnProperty(k)) {
        if (typeof obj[k] === 'string') {
          if (obj[k].indexOf(str) !== -1) {
            return true;
          }
        } else {
          queue.push(obj[k]);
        }
      }
    }
    if (queue.length) {
      for (var i = 0; i < queue.length; i++) {
        if (userMatches(str, queue[i])) {
          return true;
        }
      }
    }
    return false;
  }
}());


1
这应该可以解决问题: (请参见代码下面的说明)

const findInObject = (predicate, object) => {
  if (typeof object !== 'object') {
    throw new TypeError('Expected object but got ' + typeof object)
  }
  
  for (let key in object) {
    const value = object[key]
    switch (typeof value) {
      case 'object':
        if (findInObject(predicate, value))
          return true
      default:
        if (predicate(value))
          return true
    }
  }
  return false
}

const userContainsText = (text, user) => 
  findInObject(
    val => {
      if (typeof val !== 'string')
        return false
        
      return val.includes(text)
    },
    user
  )
  
const user = {
    id: '123abc',
    info: {
        age: 12,
        bio: 'This is my bio' 
    },
    social: {
        chatName: 'Chris',
        friends: ['friend1', 'other friend'],
        blocks: ['Creep']
    }
}

console.log(userContainsText('Chris', user))

findInObject函数承担了重要的工作。您提供一个谓词(即返回truefalse的函数,基于输入是否“通过”),以及要搜索的对象。它在对象中的每个键上运行谓词,如果提供的对象包含对象,则递归地运行谓词。如果它找到匹配项,它应该停止搜索。否则,它将遍历整个对象。 userContainsText函数使用findInObject。它提供一个检查任何字符串内容的谓词(任何其他类型都无法通过测试)。此函数接受要查找的文本和要搜索的用户对象(尽管技术上可以是任何对象,而不仅仅是“用户”对象)。

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