【问题标题】:How can I check if an object contains at least one key whose value contains a substring in JavaScript?如何检查一个对象是否至少包含一个键,其值包含 JavaScript 中的子字符串?
【发布时间】:2018-08-11 04:36:02
【问题描述】:

我想编写一个函数来检查一个对象是否至少有一个包含子字符串的值。像这样的东西(伪代码):

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,因为在 bio 中找到了子字符串 'bi':'this is my bio'。 userMatches('324d, user) 同样应该返回false。但是,usermatches('blocks', user) 应该返回 false,因为子字符串只能在其中一个键中找到,而不是在值之一中。

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

{
    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,如下面的函数。

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) 并获得一个布尔值,那就太好了。提前非常感谢!

另外,请注意,我并没有解构所有作为字符串的键。此功能的目的是根据搜索查询过滤用户,我认为让用户通过他们的 bio、id 等为其他用户搜索可能没有太大意义,而是仅通过他们的各种“名称” '。

【问题讨论】:

  • 除非这些对象的某些结构可以利用,否则不可避免地必须检查每个键以查看它是否匹配。 (排除短路true 路径,如果我们在搜索早期找到匹配项)
  • 感谢您的回答。我将使用对象的完整结构更新答案。我确实找到了一种方法,并且效果很好(我认为),但是正如您所说,我必须单独访问这些对..

标签: javascript string object substring filtering


【解决方案1】:

纯 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;
  }
}());

【讨论】:

    【解决方案2】:

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

    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
    • 我知道这是一个较旧的答案,但是有什么方法可以在阶段之后解释单词。例如,如果 userMatchesText("bi bro",user).它会出现错误。
    • 如果user 包含任何nullundefined 值,此函数将在Object.values 调用中出错。
    【解决方案3】:

    这应该可以解决问题:

    (见代码下方的解释)

    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。它提供了一个谓词来检查它获得的任何字符串的内容。 (任何其他类型未通过测试)。此函数接受要查找的文本和要搜索的用户对象(尽管从技术上讲,这可以通过任何对象,而不是具体的“用户”对象)。

    【讨论】:

      猜你喜欢
      • 2018-03-21
      • 1970-01-01
      • 2012-12-26
      • 2020-07-23
      • 2022-12-30
      • 1970-01-01
      • 2016-07-20
      • 1970-01-01
      相关资源
      最近更新 更多