我有一个短语列表,其中包含可以用空格,连字符,camelCase或PascalCase进行分类的单词。我希望能够通过只键入每个单词的几个字母来过滤这些短语,并且可能会跳过一些单词。 对于那些熟悉JetBrains IDE的人来说,它与代码完成工作方式类似。我想模拟这种行为是为了通用,而不仅仅是为了在IDE中编写代码。 (通用目的是在网站中使用它来过滤短语,也许可以与Bash一起使用它来获取文件列表,只需键入文件的一些首字母,或者我说的一些单词)。这是一个非常方便的工具,我使用了很多(在JetBrains')!
示例:
给出以下短语:
LoremIpsumDolor
sitAmetConsectetur
adipisicing-elit-sed
Do Eiusmod Tempor incididunt
以下是我想要过滤的一些典型字词:
lodo
- >返回LoremIpsumDolor
(注意这里不区分大小写,这就是我想要的方式)dotemporinc
- >返回Do Eiusmod Tempor incididunt
Do
- >返回LoremIpsumDolor
和Do Eiusmod Tempor incididunt
ac
- >返回sitAmetConsectetur
我一直在考虑如何实现这种功能,而我能想到的最好的方法是输入要过滤的单词 - 比如示例1中的lodo
,将生成一个正则表达式构造从单词的字母,由一些额外的表达式分隔,形成整个正则表达式...然后它将测试列表中的每个短语与正则表达式,并返回只匹配的那些。
我想过可能会将单词(lodo
)拆分成字母,并在每个字母之间(最开头和最后)放置以下正则表达式:([a-zA-Z][a-z]*)*
,(此解决方案,如果它可以工作,假设所有短语都是camel \ PascalCased,但完整的解决方案必须包括其他情况)。这将导致以下正则表达式:
^([a-zA-Z][a-z]*)*[lL]([a-zA-Z][a-z]*)*[oO]([a-zA-Z][a-z]*)*[dD]([a-zA-Z][a-z]*)*[oO]([a-zA-Z][a-z]*)*$
显然,这有一些来自回溯的巨大缺陷,更具体地说,我认为(但是肯定不确定),如果我可以禁用([a-zA-Z][a-z]*)*
中内部星形表达的回溯,只保留外星的回溯,它应该工作。
我希望我能够很好地解释自己。也许这个问题有一个已知的解决方案,然后我很乐意听到它。
答案 0 :(得分:4)
在考虑了几个小时之后,我使用正则表达式制作了一个解决方案,我真的认为这是一个非常合适的问题解决方案,而且它真的不那么难。
我的解决方案目前仅处理camelCase和PascalCase短语(即它只能正确过滤使用camelCase或PascalCase编写的短语),但适应其他情况应该很容易。目前,这些案例已经足够好了。
所以,这就是我想出的:
从上面的示例中提到lodo
,我们应该意识到,对于给定单词中的每个字母(l
o
d
o
),它可以是单词的第一个字母(表示它应该与大写字母匹配,或者如果它是第一个单词,它也可能是小写),或者它是单词中的下一个字母我们之前找到的(意味着它是小写的,在我们找到前一个字母之后,应该尝试匹配)。
我们还应该考虑正则表达式的行为,确切地说,是子表达式的evalutaion顺序。我们将使用以下事实:在或表达式(|
)中,首先尝试左侧,并且在e*?
(javascript)形式的表达式中,它会找到最小的可能匹配(而不是省略问号,在这种情况下它将消耗尽可能大的字符,然后我们可能会进入回溯状态,这对我们来说是不好的。)
所以,让我们构建正则表达式。对于每个字符 c ,我们构造:
如果 c 是我们的第一个字母(lodo
表示l
),那么:
(^c)
C
(^c|C)
否则:
lodo
中此参数对o
d
o
中的任何一个有效)在这种情况下必须为小写,我们构建(c)
c
字母(希望这个解释很明确)。对于所有情况,我们构建了[a-z]*([A-Z][a-z]*)*?C
。 ([a-z]*
用于消费当前单词的剩余字母,([A-Z][a-z]*)*?
用于尝试使用其他单词,如果C
不是我们下一个单词的第一个字母(请记住它可以是前面2个字的下一个字母,所以...这是我的要求))(c|([a-z]*([A-Z][a-z]*)*?C))
因此,通过这些说明,我们可以为我们心爱的lodo
构建正则表达式,这就是我们应该得到的:(^l|L)(o|([a-z]*([A-Z][a-z]*)*?O))(d|([a-z]*([A-Z][a-z]*)*?D))(o|([a-z]*([A-Z][a-z]*)*?O))
我在AngularJS项目中用一些单词对它进行了测试,看起来效果很好。我会改进它以考虑其他情况,但我认为它不应该很难。
<强>更新强>
稍微玩一下,我调整它以考虑我认为大多数可能的单词分离检测案例(通过camelCase,PascalCase,空格,连字符,下划线,实际上任何不是字母表的分隔符)字符)。这使得正则表达式更简洁,甚至可能更高效。我删除了我在原始答案中解释的大部分麻烦,并将所有[a-z]*([A-Z][a-z]*)*?
子表达式替换为仅.*?
,这是有效的,因为它没有消耗字符,直到它没有选择,这是更好的方法是先消耗字符然后回溯。
对于每个字符 c ,我们现在构造表达式:(c|.*?(C|[^a-zA-Z]c))
。然而,这可能会或可能不会引入一点回溯(取决于引擎的优化 - 如果它从正则表达式构造自动机,并且如果它最小化它),在下一个字符是非字母表的情况下,并且在它不是所需的小写字母之后的下一个字符,然后它将从[^a-zA-Z]c
表达式回溯到.*?
表达式,然后消耗(再次)非字母字符(第一个),并继续..(这意味着,在这种情况下,我们可能会在[^a-zA-Z]
和.*?
中使用该字符两次,但如果引擎优化自动机,情况可能并非如此。 / p>
现在构建的lodo
表达式为:
^(l|.*?(L|[^a-zA-Z]l))(o|.*?(O|[^a-zA-Z]o))(d|.*?(D|[^a-zA-Z]d))(o|.*?(O|[^a-zA-Z]o))
。
我知道我的问题没有得到普及,但我正在编写我想出的解决方案以供将来参考(即使它仅适用于我)。
答案 1 :(得分:0)
这是Javascript中的解决方案,与尝试使用自动完成功能相比,它非常简单。
const searchData = searchText => {
const regex = new RegExp(searchText, 'gi');
return new Promise(resolve => resolve(topMovies.filter(m => m.title.match(regex))))
};
const topMovies = [{title: "The Shawshank Redemption (1994)", rating: 9.2 },{title: "The Godfather (1972)", rating: 9.2 },{title: "The Godfather: Part II (1974)", rating: 9.0 },{title: "The Dark Knight (2008)", rating: 9.0 },{title: "12 Angry Men (1957)", rating: 8.9 },{title: "Schindler's List (1993)", rating: 8.9 },{title: "The Lord of the Rings: The Return of the King (2003)", rating: 8.9 },{title: "Pulp Fiction (1994)", rating: 8.9 },{title: "The Good, the Bad and the Ugly (1966)", rating: 8.8 },{title: "Fight Club (1999)", rating: 8.8 },{title: "The Lord of the Rings: The Fellowship of the Ring (2001)", rating: 8.8 },{title: "Forrest Gump (1994)", rating: 8.7 },{title: "Star Wars: Episode V - The Empire Strikes Back (1980)", rating: 8.7 },{title: "Inception (2010)", rating: 8.7 },{title: "The Lord of the Rings: The Two Towers (2002)", rating: 8.7 },{title: "One Flew Over the Cuckoo's Nest (1975)", rating: 8.7 },{title: "Goodfellas (1990)", rating: 8.7 },{title: "The Matrix (1999)", rating: 8.6 },{title: "Seven Samurai (1954)", rating: 8.6 },{title: "City of God (2002)", rating: 8.6 },{title: "Star Wars: Episode IV - A New Hope (1977)", rating: 8.6 },{title: "Se7en (1995)", rating: 8.6 },{title: "The Silence of the Lambs (1991)", rating: 8.6 },{title: "It's a Wonderful Life (1946)", rating: 8.6 },{title: "Life Is Beautiful (1997)", rating: 8.6 },{title: "The Usual Suspects (1995)", rating: 8.5 },{title: "Spirited Away (2001)", rating: 8.5 },{title: "Saving Private Ryan (1998)", rating: 8.5 },{title: "Léon: The Professional (1994)", rating: 8.5 },{title: "Avengers: Infinity War (2018)", rating: 8.5 },{title: "The Green Mile (1999)", rating: 8.5 },{title: "Interstellar (2014)", rating: 8.5 },{title: "American History X (1998)", rating: 8.5 },{title: "Psycho (1960)", rating: 8.5 },{title: "City Lights (1931)", rating: 8.5 },{title: "Once Upon a Time in the West (1968)", rating: 8.5 },{title: "Casablanca (1942)", rating: 8.5 },{title: "Modern Times (1936)", rating: 8.5 },{title: "The Intouchables (2011)", rating: 8.5 },{title: "The Pianist (2002)", rating: 8.5 },{title: "The Departed (2006)", rating: 8.5 },{title: "Terminator 2 (1991)", rating: 8.5 },{title: "Back to the Future (1985)", rating: 8.5 },{title: "Rear Window (1954)", rating: 8.5 },{title: "Raiders of the Lost Ark (1981)", rating: 8.5 },{title: "Whiplash (2014)", rating: 8.5 },{title: "Gladiator (2000)", rating: 8.5 },{title: "The Lion King (1994)", rating: 8.5 },{title: "The Prestige (2006)", rating: 8.5 },{title: "Memento (2000)", rating: 8.4 }];
const searchInputElement = document.querySelector('.search-input');
const resultsElement = document.querySelector('.results');
// Convert search results into UI suggestions
function showSearchResults(searchQuery) {
searchData(searchQuery).then(results => {
const html = results.map(movie => `
<li>
<span class="title">${movie.title}</span>
<span class="rating">${movie.rating}</span>
</li>
`);
resultsElement.innerHTML = html.join('');
});
}
// Pass
function handleChange() {
return showSearchResults(this.value);
}
// Register for both events
searchInputElement.addEventListener('change', handleChange);
searchInputElement.addEventListener('keyup', handleChange);
//HTML
<form class="search-form">
<input type="text" class="search-input" placeholder="Start typing a movie title...">
<ul class="results"></ul>
</form>