比较使用JavaScript或jQuery的2 div元素内的文本
我有以下2个div标签
<div class="one"> + +++ + </div>
第二个div标签
<div class="two"> + + ++++ + ++ + + + + ++ +++ + +++ ++ + +++ + ++ + + ++ + + + + ++ </div>
现在我想要find如果.one
存在于.two
。 如果是的话,我们怎么能在JavaScript中做到这一点?
更新
我想检查+
模式。 我的意思是呢
+ +++ +
存在于.two
? 该模式必须以相同的顺序。
@shub的答案似乎并没有工作。 这是JSFiddle的答案。
第1部分:
要查看数据如何匹配,可以尝试将其转换为字母而不是加号。
<div class="one"> g tuv J </div> <div class="two"> ab cdef g hi jkl m no pqr s tuv wx y zAB C DE FG HI JKLM NO </div>
你必须做这样的事情:
因为:one.innerText = g \ n tuv \ n J
这将需要变成一个正则expression式,如:/ g | tuv | J / g
要查看交叉匹配,请将第二类的内容复制并粘贴到此网站中,并删除“a”,“m”和“C”之前的空格: http : //regexr.com/3eumc
第2部分
问题是如果“tuv”在string2中移动,它将不会被“J”上方的“u”上方的“g”locking。
为了修正“J”问题之上“u”以上的“g”,我们不得不将这看作游戏棋盘的二维数据集。 这意味着将string转换为matrix(数组数组),其中每个字母加上每个空格被放入数组槽中。 像这样:
var matrix = [ // 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 = columns. Remember to add +10, +20! [a| |b| | | |c|d|e|f| | |g| |h|i| | |j| |k| |l], // row 0 [m| |n|o| |p|q|r| |s| |t|u|v| |w|x| |y| |z|A|B], // row 1 [C| |D|E| |F| |G| |H|I| |J| |K| |L| |M| |N|O] // row 2 ];
现在你可以检查看看是否:
if ((matrix[0][13] === 'g') && (matrix[1][12] === 't') && (matrix[1][13] === 'u') && (matrix[1][14] === 'v') && (matrix[2][13] === 'J')) { /* SUCCESS when all 5 letters appear & "g" is above "u" & "u" is above "J". The entire cross can move left or right, but lines 1, 2 & 3 can't shift independently of the other 3 lines. Otherwise it should fail. */ } else { // FAIL }
第3部分…
我已经解决了这个matrixsearch难题。 看到我的jsFiddle在https://jsfiddle.net/briankueck/pern32vv/
这是代码看起来的核心。 打开该jsFiddle链接上的debugging开关,查看它是如何运行的。 🙂
function getMatrix(cssSelector, canTrim) { // Built by, Clomp! @briankueck http://www.clomp.com var obj = $(cssSelector); if (obj) { var matrix = obj.text() /* Makes sure that we are selecting 3 lines, not 5 * as the example has a \n after <div ...>\n and before \n</div> */ if (canTrim) { matrix = matrix.trim(); } // Now split those 3 lines. matrix = matrix.split(/\n/); /* Trims each array in the matrix. Note: matrix[row] is a string, * but we can treat a string as an array of characters. */ if (canTrim) { // Trims each row, if desired. for (var row = 0; row < matrix.length; row++) { matrix[row] = matrix[row].trim(); } } else { // Gets rid of spaces before matrix 1 in this demo. var maxLength = 0; var space = ' '; // You can also use a period here to see how it works. var tempMatrix = []; for (var row = 0; row < matrix.length; row++) { // This cuts the rows down (vertically) from 5 to 3. if (matrix[row].trim().length > 0) { matrix[row] = matrix[row].replace(/\s/g, space); matrix[row] = matrix[row].replace(/\t/g, space); tempMatrix.push(matrix[row]); if (matrix[row].length > maxLength) { maxLength = matrix[row].length; } } } /* This loops those 3 rows (horizontally) & slices the 1st character off * each array if they are all identical & only contain spaces, which we are * tracking with the period character '.' as dots. */ var charactersToStrip = 0; for (var column = 0; column <= maxLength; column++) { for (var row = 0; row < tempMatrix.length; row++) { if (tempMatrix[row][column] !== space) { break; } else if (row === (tempMatrix.length - 1)) { charactersToStrip++; } } } /* Strips characters, without removing the space before "g" * and the space before "J". */ for (var column = 0; column < charactersToStrip; column++) { for (var row = 0; row < tempMatrix.length; row++) { tempMatrix[row] = tempMatrix[row].substring(1); } } matrix = tempMatrix; } } return matrix; } function matrixSearch(matrixOne, matrixTwo) { // Built by, Clomp! @briankueck http://www.clomp.com var space = ' '; // You can also use a period here to see how it works. // This is for " g". First we trim it, as we only want the "g" character. var searchChar = matrixOne[0].trim(); // Next we find the lock position. var firstCharPosition = matrixTwo[0].indexOf(searchChar); var matchingRows = -1; if (firstCharPosition > -1) { // This should be 1 & not 0. var matchingCharInMatrixOne = matrixOne[0].indexOf(searchChar); // Now we can find the starting index of character 0 in each row of matrixTwo: var startIndex = firstCharPosition - matchingCharInMatrixOne; // This simultaneously scans rows 1, 2 & 3 in both matricies. var matchingRows = 0; for (var row = 0; row < matrixOne.length; row++) { /* We now know both the startIndex = 11 & the lock position = 12. * So let's use them for "tuv" and " J". */ var endIndex = startIndex + matrixOne[row].length; var i = -1; for (var column = startIndex; column < endIndex; column++) { i++; if (matrixOne[row][i] !== space) { var matrixOneCharacter = matrixOne[row][i]; var matrixTwoCharacter = matrixTwo[row][column]; if (matrixOneCharacter !== matrixTwoCharacter) { break; } else if (column === (endIndex - 1)) { // Found it! matchingRows++; } } } } } // Now we can find it: var isFoundInMatrixTwo = ((matchingRows > -1) && (matchingRows === matrixTwo.length)) ? true : false; return isFoundInMatrixTwo; } var cssSelector1 = '.one'; var cssSelector2 = '.two'; var matrixOne = getMatrix(cssSelector1, false); var matrixTwo = getMatrix(cssSelector2, true); var isFound = matrixSearch(matrixOne, matrixTwo); console.log('Is matrix 1 in matrix 2? ', isFound);
请享用!
顺便说一句,圣诞快乐堆栈溢出社区从克隆!
那么,我们在这里已经有了很好的答案,但是…这是一个更多的方法。 🙂
基本上来说:filterinput,得到干净的模式/matrix(假设开始时会有两个空格 – 必须解决这个问题!),用另一个模式testing它(实际上是将HTML结构和数组从两个=>进行比较)
发生了什么事的视觉表示也在那里。
代码是暴力,可以而且应该清理(但它有点作品,哈哈):
spacer='-'; pattern=$('.one').text().replace(/ /g,spacer).split('\n'); patt=pattern.filter(function(val){ if(val.indexOf('+')>=1) { return val; } }); patt = patt.map(function(x){ return x.slice(2); }); var lgth = 0; var longest; for(var i=0; i < patt.length; i++){ // http://stackoverflow.com/questions/6521245/finding-longest-string-in-array if(patt[i].length > lgth){ var lgth = patt[i].length; longest = patt[i]; } } //console.log(longest.length); longest_sequence=longest.trim().length; matrix=[]; for(j=0;j<patt.length;j++) { // if(patt[j]!=longest) { cleaned=patt[j]+spacer.repeat(longest.length-patt[j].length); cleaned=cleaned.substr(-longest_sequence); } else { cleaned=longest.trim(); } matrix.push(cleaned); } //console.log(matrix.join('\n')); cells=[]; for(i=0;i<matrix.length;i++) { cells.push(matrix[i].split('')); $('table.small').append('<tr>'); } $( "table.small tr" ).each(function( index ) { for(j=0;j<cells[index].length;j++) { $(this).append('<td>'+cells[index][j]+'</td>'); } }); data=$('.two').text().replace(/ /g,spacer).split('\n'); data=data.filter(function(val){ if(val.indexOf('+')>=1) { return val; } }); data = data.map(function(x){ return x.slice(2); }); //console.log(data); //console.log(data.join('\n')); cells=[]; for(i=0;i<data.length;i++) { cells.push(data[i].split('')); $('table.big').append('<tr>'); } $( "table.big tr" ).each(function( index ) { for(j=0;j<cells[index].length;j++) { $(this).append('<td>'+cells[index][j]+'</td>'); } }); //comparing!!! pattern_arr=[]; $("table.small tr").each(function() { pattern_arr.push($(this).children().text().trim()) }); function arraysEqual(a1,a2) { /* WARNING: arrays must not contain {objects} or behavior may be undefined */ // console.log(JSON.stringify(a1)+':'+JSON.stringify(a2)); // console.log('________________________________________'); return JSON.stringify(a1)==JSON.stringify(a2); } count=-1; timer=setInterval(function(){ count++; sliced_arr=[]; slices=[]; $( "table.big tr" ).each(function( index ) { $(this).children('td').removeClass('search'); sliced=$(this).children('td').slice( count,count+longest_sequence ); slices.push(sliced); $(sliced).addClass('search'); sliced_arr.push($(sliced).text().trim()); if(arraysEqual(pattern_arr,sliced_arr)) { //$(sliced).addClass('found').removeClass('search'); $.each( slices, function( key, value ) { $(this).addClass('found').removeClass('search'); }); //$(slices).addClass('found').removeClass('search'); $('#status').text('Found!'); clearInterval(timer); } for(i=0;i<sliced_arr.length;i++) if(sliced_arr[i]=="") { clearInterval(timer); $('#status').text('Not found!'); break; } }); }, 1000);
.one, .two { font-size:22px; } table.big { border:1px solid #666; padding:0; border-collapse:collapse; } table.big td { border:1px solid #666; padding:5px; margin:0; } table.small { border:1px solid red; padding:0; border-collapse:collapse; } table.small td { border:1px solid red; padding:5px; margin:0; } .found { font-weight:bold; color:white; background:green; } .search { font-weight:bold; color:white; background:orange; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="one"><pre> + +++ + </pre></div> <table class="small"> </table> <div class="two"><pre> + + ++++ + ++ + + + + ++ +++ + +++ ++ + +++ + ++ + + ++ + + + + ++ </pre></div> <table class="big"> </table> <div id="status"> </div>
@ 2619 之前的post显示了对于Bliffoscope问题的兴趣。 鉴于div.one
和div.two
中的文本内容(以固定宽度字体ascii艺术风格)是从+
(它们表示活动像素)和“ “(空间) 处于非活动状态 ,我们的想法是find我们可以将
div.one
放在div.two
哪个位置,这样两个模式就形成了更多的交集,我只考虑两个活动像素的交点给定的图像。
在这个例子中, +
被o
代替以突出显示每个匹配的交集。 使用canvas
简化版本可以在这里find。
在下面的SO片段和JSFiddle演示中 ,单击“ Next
和“ Previous
链接,或者按下键盘上的箭头button,浏览匹配项。
_main.best_positions()
返回每个可能叠加的交集的数量,以任何程度的错误容忍度(按照交集的数量sorting)(首先有更多匹配)。
var PatternFinder; PatternFinder = (function(win, doc, undefined) { 'use strict'; var _main = { selectors: { object_1_pattern: ".one", background_pattern: ".two", results: ".three", next_button: ".next", previous_button: ".previous", match_score: ".intersecting_coords", match_nr: ".match_nr", }, cache: { object_text_string: '', context_text_string: '' }, init: function() { _main.cache.object_text_string = $(_main.selectors.object_1_pattern).text(); _main.cache.context_text_string = $(_main.selectors.background_pattern).text(); // Parse our images from the text strings. _main.serialized_context = _main.serialize_map(_main.cache.context_text_string); _main.serialized_object = _main.serialize_map(_main.cache.object_text_string); // Find the position of the object with larger amount of intersecting coordinates _main.best_positions = _main.get_best_position(_main.serialized_context, _main.serialized_object); _main.current_result = _main.best_positions.length - 1; // Draw initial results _main.print_output(_main.current_result); // Handle user input $(_main.selectors.next_button).click(function() { _main.current_result -= 1; _main.print_output(); }); $(_main.selectors.previous_button).click(function() { _main.current_result += 1; _main.print_output(); }); // Keyboard: Arrow keys $(document).keydown(function(e) { switch (e.which) { case 37: { // left _main.current_result += 1; _main.print_output(); break; } case 39: { // right _main.current_result -= 1; _main.print_output(); break; } default: return; } e.preventDefault(); // prevent the default action (scroll / move caret) }); }, // Highlight an intersection. // Replace "+" by "o" in coords _x, _y. highlight_match: function(_x, _y, background) { var x = 0, y = 0, i = 0, output = "", c; for (i = 0; i < background.length; i += 1) { c = background[i]; if (c == "+" && x == _x && y == _y) { output = output + "o"; } else { output = output + c; } x += 1; if (c == "\n") { x = 0; y += 1; } } return output; }, // Receive the translated serialized object, // and the original text string for the background. // Return the background text string, with the matches // between it and serialized_object highlighted. merge_and_deserialize: function(serialized_object, background) { var i; for (i = serialized_object.length - 1; i >= 0; i--) { background = _main.highlight_match(serialized_object[i][0], serialized_object[i][1], background); } return background; }, // Receive a text string like the one from the Stack Overflow ticket, // return an array of coordinates of filled in pixels (+ or space). serialize_map: function(char_map) { var x = 0, y = 0, c, i, map = []; for (i = 0; i < char_map.length; i += 1) { c = char_map[i]; if (c == "+") { map.push([x, y]); } x += 1; if (c == "\n") { x = 0; y += 1; } } return map; }, // Find number of intersections between two images (that's where the magic happens). // Found here: https://gist.github.com/lovasoa/3361645 array_intersect: function() { var a, d, b, e, h = [], l = [], f = {}, g; g = arguments.length - 1; b = arguments[0].length; for (a = d = 0; a <= g; a += 1) { e = arguments[a].length, e < b && (d = a, b = e); } for (a = 0; a <= g; a += 1) { e = a === d ? 0 : a || d; b = arguments[e].length; for (l = 0; l < b; l += 1) { var k = arguments[e][l]; f[k] === a - 1 ? a === g ? (h.push(k), f[k] = 0) : f[k] = a : 0 === a && (f[k] = 0); } } return h; }, // Translate the coordinates of a serialized image. translate: function(coords, ix, iy) { return [coords[0] + ix, coords[1] + iy]; }, // Find in which position the object has more intersections with the background. get_best_position: function(context, object) { // Calculate image dimensions var context_width = context.sort(function(a, b) { return b[0] - a[0]; })[0][0], context_height = context.sort(function(a, b) { return b[1] - a[1]; })[0][1], object_width = object.sort(function(a, b) { return b[0] - a[0]; })[0][0], object_height = object.sort(function(a, b) { return b[1] - a[1]; })[0][1]; // Swipe context, store amount of matches for each patch position. var similaritudes = [], cx, cy, intersection, translated_object; for (cx = -object_width; cx < context_width; cx += 1) { for (cy = -object_height; cy < context_height; cy += 1) { translated_object = object.map(function(coords) { return _main.translate(coords, cx, cy); }); intersection = _main.array_intersect(context, translated_object); if (intersection.length > 0) { similaritudes.push({ coords: [cx, cy], similaritudes: intersection.length }); } } } // Return coords, // sorted by those for which number of matches was greater. return similaritudes.sort(function(a, b) { return a.similaritudes - b.similaritudes; }); }, print_output: function() { var positioned_object; // Get the coordinates of one of our matches. _main.current_result = Math.max(_main.current_result, 1); _main.current_result = Math.min(_main.current_result, _main.best_positions.length - 1); var score = _main.best_positions.slice(_main.current_result)[0].similaritudes; var best_position = _main.best_positions.slice(_main.current_result)[0].coords; // Translate our image patch to the position defined by _main.current_result. positioned_object = _main.serialized_object.map(function(coords) { return _main.translate(coords, best_position[0], best_position[1]); }); // Produce merged images (background after replace). var final_image = _main.merge_and_deserialize(positioned_object, _main.cache.context_text_string); // Print image and information $(_main.selectors.results).text(final_image); $(_main.selectors.match_score).text(score); $(_main.selectors.match_nr).text(_main.best_positions.length - _main.current_result); } }; // Expose methods _main.public_methods = { init: _main.init, }; return _main.public_methods; }(window, document)); PatternFinder.init();
.one, .two { display: none; } .three { white-space: pre; font-family: "Lucida Console", Monaco, "Courier New", Courier, monospace; margin: 0 0 20px 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="one"> + +++ + </div> <div class="two"> + + ++++ + ++ + + + + ++ +++ + +++ ++ + +++ + ++ + + ++ + + + + ++ </div> <h3>Match: <span class="match_nr"></span></h3> <h5>Intersecting coordinates: <span class="intersecting_coords"></span></h5> <div class="three"></div> <nav> <a class="previous" href="#">Previous</a> <a class="next" href="#">Next</a> </nav> <p><sub>Click Next and Previous or use the keyboard arrows to see other possible matches.</sub></p>
下面的代码片段查找在“two”div中的“one”模式的所有出现,如标记中所写的那样。 结果在控制台输出中报告(行索引和行中的位置)。
积分:
- Clomp的评论帮助我理解这个问题是关于标记模式的
- Tim Down从这个答案中借用了
getIndicesOf
function find() { var i, j, k; var txtOne = $('.one').text(); var txtTwo = $('.two').text(); var linesOne = txtOne.split("\n"); // Get search patterns from "one" var patterns = getSearchPatterns(linesOne); // Get content lines from "two" var linesTwo = txtTwo.split("\n"); while (linesTwo.length > 0 && !linesTwo[0]) { linesTwo.shift(); } // Get all the positions of all patterns in all lines var searchResults = []; var patternInLines, positionsInLine; for (i = 0; i < patterns.length; i++) { patternInLines = []; for (j = 0; j < linesTwo.length; j++) { positionsInLine = getIndicesOf(patterns[i], linesTwo[j], true); patternInLines.push(positionsInLine); } searchResults.push(patternInLines); } // Get the occurrences of all patterns at the same position on consecutive lines var results = []; var firstPatternInLine, firstPatternPosition, patternInLine, found; var firstPattern = searchResults[0]; for (j = 0; j < linesTwo.length - patterns.length; j++) { firstPatternInLine = firstPattern[j]; for (k = 0; k < firstPatternInLine.length; k++) { firstPatternPosition = firstPatternInLine[k]; found = true; for (i = 1; i < patterns.length; i++) { patternInLine = searchResults[i][j + i]; if (patternInLine.indexOf(firstPatternPosition) < 0) { found = false; break; } } if (found) { results.push({ line: j, position: firstPatternPosition }) } } } // Display results for (i = 0; i < results.length; i++) { console.log(results[i]); } if (results.length < 1) { console.log("No occurrence found"); } } // Trim the search lines to get the minimal search "block" function getSearchPatterns(lines) { var items = []; var result = []; var i, txt, offset, item; var minOffset = 1000000; var maxLength = 0; for (i = 0; i < lines.length; i++) { txt = lines[i].trim(); if (txt) { offset = lines[i].indexOf(txt); items.push({ str: txt, offset: offset }); minOffset = Math.min(offset, minOffset); } } for (i = 0; i < items.length; i++) { item = items[i]; item.offset -= minOffset; maxLength = Math.max(item.offset + item.str.length, maxLength); } for (i = 0; i < items.length; i++) { item = items[i]; result.push(paddRight(paddLeft(item.str, item.offset), maxLength)); } return result; } function paddLeft(str, count) { var padding = ""; for (var i = 0; i < count; i++) { padding += " "; } return padding + str; } function paddRight(str, length) { var result = str; while (result.length < length) { result += " "; } return result; } // Find all positions of search string in string // By Tim Down at https://stackoverflow.com/a/3410557/1009922 function getIndicesOf(searchStr, str, caseSensitive) { var searchStrLen = searchStr.length; if (searchStrLen == 0) { return []; } var startIndex = 0, index, indices = []; if (!caseSensitive) { str = str.toLowerCase(); searchStr = searchStr.toLowerCase(); } while ((index = str.indexOf(searchStr, startIndex)) >= 0) { indices.push(index); startIndex = index + searchStrLen; } return indices; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="one"> a bcd e </div> <div class="two"> ++ + ++ ++++ + ++ + + + a + + ++++ a ++ + + +bcd + a +++ + bcd ++ + +++ e +bcd + + ++ e + + + ++ ++ + e ++++++++++++++++++++++ </div> <button onclick="find()">Test!</button>
你的jQuery代码将看起来像这样。 你可以用这个逻辑玩更多的东西
jQuery().ready(function(){ var lines = $(".two").text().split("\n"); for(i=1;i<=lines[1].length;i++){ if(lines[1][i]=='+' && lines[2][i-1]!='undefined' && lines[1][i]==lines[2][i-1] && lines[2][i]!='undefined' && lines[1][i]==lines[2][i] && lines[2][i+1]!='undefined' && lines[1][i]==lines[2][i+1] && lines[3][i]!='undefined' && lines[1][i]==lines[3][i] ){ console.log('exists'); } } });
这是小提琴: https : //jsfiddle.net/ahmadasjad/12eqhs7L/5/
在以前的文章中有相当有趣的想法,我喜欢增加function更紧凑的方法(它使用lodash)。 主要思想是修剪匹配的文本,并与目标文本上的切换窗口( 剪切 )进行比较。 在该函数之上还返回从左边find匹配的位置。 请参阅工作小提琴 :
function findText(text, search) { const width = maxWidth(search)-minTrim(search); const matcher = cut(search, minTrim(search),width).join(''); return _.range(text[1].length) // create array of possible matches .map(col=>cut(text, col, width).join('')) .indexOf(matcher)+1; // and match with matcher } // Returns left padding size, eg 3 in the example function minTrim(t) { return _.min(t.filter(s=>!!s).map(s=>s.length-_.trimStart(s).length)) } // Returns window within $text at $start position with $width function cut(text, start, width) { return text.map(s=>_.padEnd(s.substr(start,width),width)) } // Returns maximum width of the line within text function maxWidth(text) { return _.max(text.map(s=>s.length)) }
函数findText可以像这样使用:
const two=document.getElementsByClassName("two")[0].innerHTML.split('\n'); const one=document.getElementsByClassName("one")[0].innerHTML.split('\n'); alert((pos=findText(two,one)) ? `found at position ${pos}` : "not found");
选项2如果string只包含'+'且长度不超过64个字符,我们可以将上面的函数转换成位掩码匹配。 例如,将每个string转换为二进制数字,然后移动目标string,应用search掩码(例如上例中的窗口)并比较数字。 看到工作小提琴 :
function findTextBin(text,search) { const toBin=str=>str.split('') .reduce((res,c)=>res<<1|(c==='+'?1:0),0) let one=search.map(toBin) let mask=toBin(_.max(one).toString(2).split('').map(c=>'+').join('')) let two=text.map(toBin) let length=_.max(text.map(str=>str.length)) for(let i=length; i; i--,two=two.map(num=>num>>1)) if(two.every((num,row)=>(num&mask)===one[row])) return i-mask.toString(2).length; return false; }
只是一个build议,但你可以散列的内容来比较它们。 此代码使用CryptoJS库生成MD5哈希
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/md5.js"></script> <script> var div1 = CryptoJS.MD5( $('.one').text() ); var div2 = CryptoJS.MD5( $('.two').text() ); if(div1 === div2) { // your code here } </script>
假设两行中的行数相同,下面的解决scheme可以简化查找模式。
前提条件:
这个解决scheme需要这个模式,而且要形成的目标如下:
var pattern = [ ' + ', '+++', ' + ' ]; var target = [ '+ + ++++ + ++ + + +', '+ ++ +++ + +++ ++ + +++', '+ ++ + + ++ + + + + ++ ' ];
我认为一个简单的JavaScript操作可以做到这一点 – 逐行阅读HTML,并将其附加到数组。 如果需要,我也可以提供该代码。
怎么运行的:
代码首先决定pattern
的宽度,然后继续在target
数组中find这种宽度逐列的模式。 当检测到较低宽度的图案或find匹配时,循环停止。
代码:
// Our core function function matches(target, pattern) { if (pattern.length > target.length) { return false; } var patternWidth = findPatternWidth(pattern); var startIndex = 0; var currentPattern = getNextPattern(target, startIndex, patternWidth); while (patternValid(currentPattern, patternWidth)) { if (identicalArrays(currentPattern, pattern)) { return true; } startIndex++; currentPattern = getNextPattern(target, startIndex, patternWidth); } return false; } // -*-*-*-*-*- HELPER FUNCTIONS -*-*-*-*-*-* // Finds the width of the pattern we want to match function findPatternWidth(pattern) { var maxWidth = 0; for (var i = 0; i < pattern.length; i++) { if (pattern[i].length > maxWidth) { maxWidth = pattern[i].length; } } return maxWidth; } // Finds the next suitable pattern, starting with an index, of a maximum width function getNextPattern(target, startIndex, maxWidth) { var result = []; for (var i = 0; i < target.length; i++) { result.push(target[i].substr(startIndex, maxWidth)); } return result; } // Checks if two non-nested arrays are identical function identicalArrays(source, target) { if (source.length !== target.length) { return false; } for (var i = 0; i < source.length; i++) { if (source[i] !== target[i]) { return false; } } return true; } // Checks if a given pattern is of given width function patternValid(pattern, maxWidth) { for (var i = 0; i < pattern.length; i++) { if (pattern[i].length < maxWidth) { return false; } } return true; }
我相信这种方法可以扩展到摆脱这种假设the number of rows are same in both
。
要find另一个string中的模式,首先find相互之间的相对位置。 然后检查第二个string中的加号是否处于相同的相对位置。
findCoordinates
函数查找模式中相对于模式string中第一个加号的位置。 对于这个模式,
+ +++ +
最上面的+
在(0,0)。 第二行中的第一个+
在(-1,1)处,因为它在第一个+
下面一行和第一个的左边。 类似地,其他的脉冲分别位于(0,1),(1,1)和(0,2)。
hasPattern
函数使用相对坐标来检查模式是否出现在第二个string中。 对于第二个string中的每个+
,它会检查相应位置是否有其他+
字符与该模式相匹配。 对于示例模式,函数将检查一个字符是否是一个加号。 如果是+
,则检查左下方,右下方,右下方和下方两行中的字符。 如果这些字符也是加号,那么函数返回true。
function findCoordinates(string) { var rows = string.split('\n'); var coordinates = []; var first = null; for (var i = 0; i < rows.length; i++) { for (var j = 0; j < rows[i].length; j++) { if (rows[i][j] === '+') { if (first === null) { first = {x:j, y:i}; } coordinates.push({x:j-first.x, y:i-first.y}); } } } return coordinates; } function hasPattern(string, coordinates) { var rows = string.split('\n'); var matches = 0; var coordinate = null; for (var i = 0; i < rows.length; i++) { for (var j = 0; j < rows[i].length; j++) { if (rows[i][j] === '+') { matches = 0; for (var k = 0; k < coordinates.length; k++) { coordinate = coordinates[k]; if (rows[i + coordinate.y] && rows[i + coordinate.y][j + coordinate.x] === '+') { matches++; } } if (matches === coordinates.length) { return true; } } } } return false; } var one = document.querySelector('.one'); var two = document.querySelector('.two'); console.log(hasPattern(two.textContent, findCoordinates(one.textContent)));
div { font-family: monospace; white-space: pre; }
<div class="one"> + +++ + </div> <div class="two"> + + ++++ + ++ + + + + ++ +++ + +++ ++ + +++ + ++ + + ++ + + + + ++ </div>
$('.one').diffString($('.two').html());
如果test pattern
和source pattern
的行数不同,则此解决scheme将不起作用。
这个想法是创build一个模式的连续列的string。 然后,我们可以使用indexOf
轻松检查模式是否存在
例如,一个这样的模式:
1 2 3 + + + + + +
变成"+ + +++ +"; // equivalent of ["+ +"," ++","+ +"].join("");
"+ + +++ +"; // equivalent of ["+ +"," ++","+ +"].join("");
这里是小提琴https://jsfiddle.net/flyinggambit/vcav3c46/
function getVerticalPattern(pattern){ // split the pattern in to an array, each item represents each line var pattern = pattern.split("\n"); var numLines = pattern.length; // Find the number of lines // Find the longest string var longestString = 0; for(var i=0; i<pattern.length; ++i){ longestString = pattern[i].length; } // Rearrange the pattern var newPattern = []; for (var i=0; i<longestString; i++){ for (var j=0; j<numLines; j++){ if(pattern[j] && pattern[j].length){ // sometimes the split was creating empty strings "" newPattern.push(pattern[j][i]); } } } return newPattern.join(""); } function findPattern(testPattern, srcPattern){ return (getVerticalPattern(srcPattern)).indexOf(getVerticalPattern(testPattern)); } var srcPattern = document.getElementsByClassName("two")[0].innerHTML; var testPattern = document.getElementsByClassName("one")[0].innerHTML; var result = findPattern(testPattern, srcPattern); if(result !== -1){ console.log("pattern found"); }else{ console.log("pattern not found"); }
<pre class="one"> + +++ + </pre> <pre class="two"> + + ++++ + ++ + + + + ++ +++ + +++ ++ + +++ + ++ + + ++ + + + + ++ </pre>
这是快速和丑陋的,仍然有一些错误检查和优化尚待完成…但足以显示的概念:
var pattern = [ '+ + ', '+ ++', '+ ++' ]; var target = [ '+ + ++++ + ++ + + +', '+ ++ +++ + +++ ++ + +++', '+ ++ + + ++ + + + + ++ ' ]; function getAllIndexes(arr, val) { var indexes = [], i = -1; while ((i = arr.indexOf(val, i+1)) != -1){ indexes.push(i); } return indexes; } function checkNextRow(pattern, target, pRow, tRow) { var matchePos = target[i].indexOf(pattern[0]); } function test(pattern, target) { //get pattern hights for later var pH = pattern.length; var tH = target.length; //target should have equal or more rows if (tH < pH) return 'not found'; //which is the lowest row of the target where top row of the pattern can be matched? tLastTop = tH - pH; //function to check row of pattern function checkRest(pRow, tRow, hPosMatch) { console.log('searching for '+pattern[pRow]+' in '+target[tRow]); var matchPos = target[tRow].indexOf(pattern[pRow], hPosMatch); console.log('search result>>>>'+matchPos); if (matchPos >= 0 && matchPos === hPosMatch) { if (pRow === pH-1) { console.log('last row of pattern matched'); return true; //last row of pattern matched } else { console.log('calling checkRow from itself'); return checkRest(pRow+1, tRow+1, hPosMatch); } } else { console.log('pattern not found in row, returning false',hPosMatch, matchPos); return false; } } //search for top row of pattern for (i = 0; i <= tLastTop; i++) { //get all accurance indexes of patern's first row var matches = getAllIndexes(target[i], pattern[0]); console.log("matches",matches); if (matches.length <= 0) continue; //try other rows for each accurance position for (h = 0; h <= matches.length; h++) { var hPosMatch = matches[h]; console.log('calling checkRow from iterator'); var result = checkRest(1,i+1,hPosMatch); if (result) return true; } } return false; } console.log(test(pattern, target));
I skipped loading DIV content into pattern/target vars as it seems trivial. Also I assumed spaces are also important for pattern, meaning ' + +
' does not match ' +++
'
JSBin is here: http://jsbin.com/kayeyi/edit?js,console
To not overcomplicate things I made some assumptions on the inputs:
- Number of lines in inputs are equal
- Any empty line from inputs can be omitted
- Every non-empty line of inputs should contain whitespace characters at "missing" places
- The whitespace character in both inputs is the same and it's NOT a line break
Besides of collecting data from the DOM the solution goes through following steps:
- Both pattern and input are converted into arrays of strings, each string for a line. Arrays must have same length ( assumption 1 )
- For each pair of lines list of possible matches are collected – list of all indices in input string on which pattern string is included
- Result lists are flattened into single list and all indices are counted. Now for each possible index we have number of lines where match is successful
- Filter the result to leave only indices with maximum count
There is also a working fiddle of the solution
function indexOf(pattern, input){ if(pattern.length !== input.length) throw 'Works only for same number of lines'; var counts = [].concat(...input.map((s,i) => allMatches(pattern[i],s))).reduce((r,e) => (r[e] = (r[e] || 0) + 1, r), {}); //find all matches for all lines and flatten the result var stops = Object.keys(counts).filter(k => counts[k] === pattern.length); //get only those that span across all the lines return stops[0] || -1; //first found or -1 if empty } function allMatches(substr, str){ var result = [], index = 0; while(~(index = str.indexOf(substr, index))) // ~(-1) is falsy result.push(index++); return result; } function readContent(element){ return (element.value || element.textContent).split(/[\r\n]+/).filter(s => s.length); //get all non-empty lines } function showResult(pattern, input){ var chars = Array(input[0].length).fill('\xa0'); // chars[indexOf(pattern, input)] = '^'; document.querySelector('.result').textContent = chars.join(''); } function updater(){ showResult( readContent(document.querySelector('.one')), readContent(document.querySelector('.two')) ); } document.querySelector('.one').addEventListener('input', updater); document.querySelector('.two').addEventListener('input', updater); updater();
.one, .two, .result{ padding: 0; margin: 0; font-family: monospace; width: 100%; font-size: 1rem; }
<textarea class="one" rows="4"> + +++ + </textarea> <textarea class="two" rows="4"> + + ++++ + ++ + + + + ++ +++ + +++ ++ + +++ + ++ + + ++ + + + + ++ </textarea> <div class="result"></div>