【问题标题】:Dynamically match multiple words in a string to another one using regExp使用 regExp 将字符串中的多个单词动态匹配到另一个单词
【发布时间】:2017-02-20 13:36:11
【问题描述】:

我们都知道 regExp 可以使用两个字符串中的一个共同词作为匹配标准来匹配两个字符串。这段代码会将 "I like apples" 匹配到 "apples",因为它们都有共同的“apples” .

var str1 = "apples";
var test1 = "I like apples";
var reg1 = new RegExp(str1, "i");

if (test1.match(reg1)) {
    console.log(str1 + " is matched");
}

但是如果不是使用单个常用词(或字符串的公共部分)匹配字符串,我需要使用多个常用词匹配两个字符串,这些常用词可能被其他词分隔?让我给你举个例子:

test2 = "我喜欢 juicy 红色 fruits" 应该匹配 str2 = "juicy fruits " 因为test2 包含所有str2 第二个关键词(参见下面的对象),即使它之间还有其他词。

棘手的部分是我无法确切知道字符串是什么,所以我必须动态匹配它们。我不知道的字符串是用户输入字段的值,有很多可能。此值将与存储在此对象中的字符串匹配:

var str2 = {
    "red apples": "fruits",
    "juicy fruits": "price : $10"
};

因此,无论用户在输入字段中键入什么,当且仅当它包含对象属性之一的所有单词时,它都必须是匹配的。因此,在本例中,“juicy red and成熟fruits”应该匹配第二个对象属性,因为它包含了它的所有关键字。 p>

编辑:我的目标是输出与我匹配的字符串相关的值。在我的示例中,如果第一个键匹配,则应输出“fruits”。如果匹配 'juicy fruits',它应该输出 'price : $10'。获取与对象键关联的字符串是用户必须使用输入搜索它们的原因。

是否可以使用纯 javascript 使用 regExp 来做到这一点?

这是我(糟糕地)尝试做的事情:https://jsfiddle.net/Hal_9100/fzhr0t9q/1/

【问题讨论】:

  • 为您要检查的每个单词构建一个前瞻列表,例如(?=.*\bjuicy\b)(?=.*\bfruits\b)
  • str2 是一个以 "red apples""juicy fruits" 作为键的对象。 test2 只有"juicy", "red", and "apples"。我不明白“test2 包含所有str2 单词......”的说法。我误会了什么?
  • @wcarroll :我的句子措辞错误:我想说test2 包含所有str2 第二个关键词。我编辑了我的帖子,感谢您指出。
  • @Hal_9100 将字符串化的数字作为str2 对象中的值是怎么回事?那部分我还不清楚。这种数据结构真的有必要吗?

标签: javascript regex


【解决方案1】:

对于您所描述的情况,您甚至不需要正则表达式。如果将搜索字符串拆分为空格;您可以检查要匹配的每个单词是否包含在搜索单词数组中。

function matchesAllWords(searchWords, inputString) {
  var wordsToMatch = inputString.toLowerCase().split(' ');

  return wordsToMatch.every(
    word => searchWords.indexOf(word) >= 0);
}

在下面的 sn-p 中,输入 input 会导致重新计算 searchWords。然后为匹配的li 元素赋予.match 类以突出显示它们。

function updateClasses(e) {
  var searchWords = e.target.value.toLowerCase().split(' ');

  listItems.forEach(listItem => listItem.classList.remove('match'));

  listItems.filter(
      listItem =>
      matchesAllWords(searchWords, listItem.innerText))
    .forEach(
      matchingListItem =>
      matchingListItem.classList.add('match'));
}

function matchesAllWords(searchWords, inputString) {
  var wordsToMatch = inputString.toLowerCase().split(' ');
  
  return wordsToMatch.every(
    word => searchWords.indexOf(word) >= 0);
}

function searchProperties(e) {
  var searchWords = e.target.value.toLowerCase().split(' ');

  for (var property in propertiesToSearch) {
    if (matchesAllWords(searchWords, property)) {
      console.log(property, propertiesToSearch[property]);
    }
  }
}

var propertiesToSearch = {
  "red apples": 1,
  "juicy fruit": 2
};

listItems = [].slice.call(
  document.getElementById('matches').querySelectorAll('li')
);

document.getElementById('search').addEventListener('keyup', updateClasses);
document.getElementById('search').addEventListener('keyup', searchProperties);
.match {
  color: green;
}
<label for="search">
  Search:
</label>
<input type="text" name="search" id="search" />

<ul id="matches">
  <li>red apples</li>
  <li>juicy fruits</li>
</ul>

更新要使用这种实现来搜索属性,请使用for .. in 循环,如下所示。再一次,在上下文中查看这个工作的 sn-p。

function searchProperties(e) {
  var searchWords = e.target.value.toLowerCase().split(' ');

  for (var property in propertiesToSearch) {
    if (matchesAllWords(searchWords, property)) {
      console.log(property, propertiesToSearch[property]);
    }
  }
}

var propertiesToSearch = {
  "red apples": 1,
  "juicy fruit": 2
};

【讨论】:

  • 谢谢,您的回答非常好。问题是我需要获取与我匹配的字符串相关联的字符串/值(在我的演示中由数字表示)。这就是为什么用户必须首先搜索它们。我试图调整您的解决方案以使其与对象中的键而不是 DOM 元素中的文本一起使用,但我无法使其正常工作。我不是在尝试搜索 DOM,而是通过对象的键来在匹配完成后获取它们的值。有没有办法用你的方法做到这一点?再次感谢您在回答中付出的时间和精力
  • 你可以使用for...in循环遍历属性,检查属性名称中是否输入matchesAllWords,如果是,则输出相应的值(str2[key]或其他)跨度>
  • @Hal_9100 查看更新 - 正如 qxz 建议的那样,可以使用 matchesAllWords 使用 for ... in 循环搜索键。
  • 谢谢,除了解决匹配问题之外,您的代码几乎完全符合我的要求:当且仅当输入值包含所有单词时,如何匹配对象键,即使尽管它们被其他词分开?您的代码的工作方式是匹配“红色水果”,即使我只输入“r”,如果我在输入中的“红色”和“水果”之间插入其他单词,它也不再匹配键。这就是我被困住的地方。如何编写一个函数,如果它包含所有单词,则仅将对象键与字符串匹配,即使用户在其间键入单词?
  • @Hal_9100 我现在再次更新,以便matchesAllWords 检查匹配字符串的所有单词是否包含在搜索中给出的单词列表中。
【解决方案2】:

我认为您可能会从将数据结构从对象字面量转换为数组中受益,如下所示:

const wordGroupings = [
  'red apples',
  'juicy fruits'
];

我写了一个processString 函数,它接受一个字符串和一些词组作为输入,并返回一个词组的过滤列表,其中词组中的每个词都出现在输入字符串中。

换句话说,让我们假设测试字符串是:

const testString = 'I like big, frozen whales.';

让我们进一步想象您的 wordGroupings 数组看起来像这样:

const wordGroupings = [
  'whales frozen',
  'big frozen whales toucan',
  'big frozen',
  'sweaty dance club',
  'frozen whales big'
]

调用processString(wordGroupings, testString) 的输出将是:

[
  'whales frozen',
  'big frozen',
  'frozen whales big'
]

如果这是你要找的,这里是processString(..)的实现:

function processString(wordGroupings, testString) {
  var regexes = wordGroupings.map(words => ({
      origString: words,
      regex: new RegExp(words.replace(/\s+/g, '|'), 'g'),
      expCount: words.split(/\s+/g).length
    })
  );

  filtered = regexes.filter(({regex, expCount}) =>
    (testString.match(regex) || []).length === expCount
  );

  return filtered.map(dataObj => dataObj.origString);
}

希望这会有所帮助!

【讨论】:

  • 非常感谢您的回答,这个功能真的很棒。这不是我一开始就想做的,但这确实有助于进一步的项目。我实际上使用了一个对象,因为我的目标是输出与我匹配的字符串关联的值(我奇怪地用字符串化的数字表示;我知道这部分在我的问题中令人困惑,对此感到抱歉)。获取与字符串关联的值是用户必须使用输入搜索它们的原因。是否可以使用对象而不是数组来调整您的解决方案?
  • @Hal_9100 我不确定我理解你所说的"...the value associated to the strings..." 是什么意思。你说的价值是什么意思?在您的问题的上下文中,字符串如何具有价值?是它向输入字符串注册的成功匹配数吗?
  • 按值是指与我正在匹配的对象键关联的字符串 在我的示例中,当匹配“红苹果”时,它应该输出其关联的字符串“水果”。这就是我使用对象的原因:var str2 = { "red apples": "fruits", "juicy fruits": "price : $10" };。如果匹配 'juicy fruits',它应该输出 'price : $10'。我编辑了我的问题以使其更清楚。
  • @Hal_9100 回答这个问题开始感觉像是击中了一个移动的目标。在发布之前,您应该确保您的问题措辞恰当并且要求易于理解。
猜你喜欢
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 2010-12-15
  • 2015-04-08
  • 2021-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多