如何确定一个字符串是否以另一个字符串开头

时间:2019-02-09 13:47:05

标签: javascript arrays string

我有一个短语列表:

[
  "according to all known laws of aviation",
  "time flies when you're having fun..."
  ...
]

我想在此列表中找到与输入字符串结尾匹配的所有短语。例如:

getNext("did you know that time flies w")

应该在列表中进行搜索,以匹配以did you know that time flies w结尾的任何字符串。例如,它应返回至少包含time flies when you're having fun的列表,因为它以time flies w结尾,这是该短语的开头。

我想不出任何合理的方法来做到这一点。

我唯一想到的就是遍历输入中的每个字符,并检查它是否与词组的开头匹配。如果是这样,请增加一个计数器。如果在循环结束时它大于1(或其他一些数字),则在另一个列表的短语处以及匹配数。从那里,我可以对列表进行排序,以便首先给出最接近的匹配,最后给出不那么接近的匹配。

肯定有更好的方法可以做到这一点吗?

有关背景知识,您可以查看this Repl.it projectthis very similar Python question

3 个答案:

答案 0 :(得分:1)

如果只想匹配某个字符串数组的开头,则可以使用filterstartsWith函数对。

  let test = "a ";
  const items = phrases.filter(phrase => phrase.startsWith(test));

请注意,如果您不在乎,并且您的字符串都是小写字母,请使用.toLowerCase()使其与“ I”和“ i”匹配;

  let test = "a ";
  const items = phrases.filter(phrase => phrase.toLowerCase().startsWith(test));

注意:我编辑了这个实时示例,以通过复选框显示“包含”,以防万一有助于说明替代方案。

这是一个实时示例:

let phrases = ["according to all known laws of aviation", "a blessing in disguise", "a bunch", "a dime a dozen", "beat around the bush", "better late than never", "bite the bullet", "break a leg", "call it a day", "cut somebody some slack", "cutting corners", "dead tired", "easy does it", "excuse me", "get it out of my system", "get it out of your system", "get out of hand", "get something out of my system", "get something out of your system", "get your act together", "give someone the benefit of the doubt", "go back to the drawing board", "good idea", "hang in there", "hit the nail on the head", "hit the sack", "how does that sound?", "i don't know", "i don't understand", "i love you", "i owe you one", "i'm fine", "i'm fine, thanks", "i'm really sorry", "i'm sorry", "it cost an arm and a leg", "it costs an arm and a leg", "it'll cost an arm and a leg", "it will cost an arm and a leg", "it's not rocket science", "i've never given it much thought", "kill two birds with one stone", "killing two birds with one stone", "let you off the hook", "let me off the hook", "look, it's", "make a long story short", "miss the boat", "never gonna give you up", "no pain,  no gain ", "on the ball ", "once upon a time ", "pull your leg ", "pulling your leg ", "pull yourself together ", "proof of concept ", "so far so good ", "so much ", "speak of the devil ", "thanks so much ", "thank you so much ", "that's the last straw ", "the best of both worlds ", "this is a test", "time flies when you're having fun", "to be honest", "to get bent out of shape", "to kill a mockingbird", "to make matters worse", "under the weather", "watch out", "we'll cross that bridge when we come to it ", "whatever floats your boat", "whatever you say ", "wrap your head around something", "you can say that again", "your guess is as good as mine", "there's no such thing as a free lunch", "throw caution to the wind", "you can't have your cake and eat it too ", "have your cake and eat it too", "judge a book by its cover ", "book by its cover", "last straw", "shut up", "be quiet", "how are you?", "never gonna give you up", "water under the bridge", "let you down", "birds and the bees", "pair of trainers", "i'd really like", "i wouldn't mind", "i could do with"];

$('#mywords').on('input change', function(event) {
  let grp = $("<div></div>");
  let test = $(this).val();
  $('#testing').html(test);
  const items = phrases.filter(phrase => phrase.startsWith(test));
  $.each(items, function(index, value) {
    let rowItem = $("<div class='row-item'></div>").text(index + ". " + value);
    grp.append(rowItem);
  });
  $('#results-list').html(grp.children());
});

function dochange(event) {
  let grp = $("<div></div>");
  let test = $('#mywords').val();
  $('#testing').html(test);
  let items = [];
  if (!$('#mywords-contain').prop('checked')) {
    items = phrases.filter(phrase => phrase.toLowerCase().startsWith(test));
  } else {
    items = phrases.filter(phrase => phrase.toLowerCase().includes(test));
  }
  $.each(items, function(index, value) {
    let rowItem = $("<div class='row-item'></div>").text(index + ". " + value);
    grp.append(rowItem);
  });
  $('#results-list').html(grp.children());
}

$('#mywords-contain').on('change', dochange);
$('#mywords').on('input change', dochange);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<label>Enter something:<input id="mywords" type="text"/></label><label>Check For contains:<input id="mywords-contain" type="checkbox"/></label><span id="testing">(empty)</span>
<div id="result-group">Result found:
  <div id="results-list"></div>
</div>

答案 1 :(得分:1)

我在这里回答时感到非常沮丧,没有比暴力破解更好的东西了……我真的很乐于经历一种更聪明的方式,并且仍然希望有人能做到。

但是对于您的应用程序,必须在jojonas的算法上添加一些优化。您可能将在每次击键时执行此检查,并且由于它是自动完成的,因此输入的长度可能会很快增加。

第一个优化是将输入切成所检查字符串的长度。 给定长度为8的输入以及要比较长度为3的字符串,则前5次迭代不可能返回匹配项

input:   "aaaaaaaa" (8)
compare:      "aaa" (3)
               ^- first possible match for compare.startsWith()

通过将迭代器初始化为max(input.length - compare.length, 0),可以很容易地做到这一点。

此算法的第二个优化方法是在compare的其余部分内搜索input的第一个字母的第一个索引。只要不是compare的第一个字符,就不必遍历每个字符,我们可以确定compare.startsWith将返回false。然后,我们可以重复进行,直到找到正确的余数为止,然后再在整个过程中删除很多循环。

const phrases = ["according to all known laws of aviation", "a blessing in disguise", "a bunch", "a dime a dozen", "beat around the bush", "better late than never", "bite the bullet", "break a leg", "call it a day", "cut somebody some slack", "cutting corners", "dead tired", "easy does it", "excuse me", "get it out of my system", "get it out of your system", "get out of hand", "get something out of my system", "get something out of your system", "get your act together"];


inp.oninput = e => {
  const overlaps = getOverlaps(inp.value, phrases);
  makeList(overlaps);
  if(logged) console.clear();
};
let logged = false;
inp.oninput(); 
logged = true;

// "abcde", "def"
function getOverlaps(input, list) {
  return list.filter(compare => {
    // single logger, just for demo
    const log = (...args) => !logged && compare === "beat around the bush" && console.log.apply(console, args);

    // search only in possible area
    let i = Math.max(input.length - compare.length, 0); // 2
    
    log('initial i:', i);
    
    const first_letter = compare[0]; // "d"
    let remain = input.substr(i); // "cde"
    
    log('initial remain:', remain);
    
    while(i < input.length) {
      // jump to first letter
      let index = remain.indexOf(first_letter); // 1

      log('in-loop index:', index);

      if(index < 0) { // not found
        return false;
      }
      i += index; // 3
      remain = input.substr(i); // "de"
      
      log('in-loop remain:', remain);
      
      if(compare.startsWith(remain)) { // found
        return true; // =>
      }
      
      // wasn't this one, move by one char
      // (will jump to next occurence of first_letter) at next iteration
      i++;
      remain = input.substr(i);
    }
      
  });

}

function makeList(items) {
  list.innerHTML = '';
  items.forEach(e => 
    list.appendChild(
      document.createElement('li')
    ).textContent = e
  );
}
body{padding-bottom: 120px}
<input id="inp" value="according to all known laws of aviation to bring a beat a" autofocus>
<ul id="list"></ul>

答案 2 :(得分:0)

在对Python语言进行了一些研究之后,我将Python问题的答案转换为Javascript:

function OverlapTest(input, compare) {
    for (var i = 0; i < input.length; i++) {
        if (compare.startsWith(input.substring(i))) {
            return true;
        }
    }

    return false;
}

如果字符串的开头和结尾重叠,则返回true,否则返回false