كيفية التحقق مما إذا كانت سلسلة تحتوي على سلسلة فرعية في جافاسكريبت؟
عادةً ما كنت أتوقع وجود طريقة String.contains()، لكن لا يبدو أن هناك واحدة.
ما هي طريقة معقولة للتحقق من هذا؟
الإجابة رقم 1
قدم ECMAScript 6 String.prototype.includes:
const string = "foo";
const substring = "oo";
console.log(string.includes(substring)); // trueString.prototype.includes حساس لحالة الأحرف (case-sensitive) وغير مدعوم من قبل إنترنت إكسبلورر (Internet Explorer) بدون إضافة بديلة (polyfill).
في بيئات ECMAScript 5 أو الأقدم، استخدم String.prototype.indexOf، والذي يُرجع -1 عندما لا يمكن العثور على جزء من السلسلة:
var string = "foo";
var substring = "oo";
console.log(string.indexOf(substring) !== -1); // trueالإجابة رقم 2
هناك String.prototype.includes في ES6:
"potato".includes("to");
> true
لاحظ أن هذا لا يعمل في Internet Explorer أو بعض المتصفحات القديمة الأخرى التي لا تدعم ES6 أو تدعمها بشكل غير كامل. لجعله يعمل في المتصفحات القديمة، قد ترغب في استخدام مترجم مثل Babel، أو مكتبة تحويل مثل es6-shim، أو هذا التعويض المساعد من MDN:
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
الإجابة رقم 3
بديل آخر هو KMP (كنوث-موريس-برات).
تبحث خوارزمية KMP عن سلسلة فرعية بطول m في سلسلة بطول n في أسوأ حالة في وقت O(n+m)، مقارنة بأسوأ حالة O(n⋅m) للخوارزمية البسيطة، لذلك قد يكون استخدام KMP معقولًا إذا كنت تهتم بتعقيد الوقت في أسوأ الحالات.
إليك تنفيذ بلغة جافاسكريبت من مشروع Nayuki، مأخوذ من https://www.nayuki.io/res/knuth-morris-pratt-string-matching/kmp-string-matcher.js:
// Searches for the given pattern string in the given text string using the Knuth-Morris-Pratt string matching algorithm.
// If the pattern is found, this returns the index of the start of the earliest match in 'text'. Otherwise -1 is returned.
function kmpSearch(pattern, text) {
if (pattern.length == 0)
return 0; // Immediate match
// Compute longest suffix-prefix table
var lsp = [0]; // Base case
for (var i = 1; i < pattern.length; i++) {
var j = lsp[i - 1]; // Start by assuming we're extending the previous LSP
while (j > 0 && pattern[i] !== pattern[j])
j = lsp[j - 1];
if (pattern[i] === pattern[j])
j++;
lsp.push(j);
}
// Walk through text string
var j = 0; // Number of chars matched in pattern
for (var i = 0; i < text.length; i++) {
while (j > 0 && text[i] != pattern[j])
j = lsp[j - 1]; // Fall back in the pattern
if (text[i] == pattern[j]) {
j++; // Next char matched, increment position
if (j == pattern.length)
return i - (j - 1);
}
}
return -1; // Not found
}
console.log(kmpSearch('ays', 'haystack') != -1) // true
console.log(kmpSearch('asdf', 'haystack') != -1) // false