Skip to content

Instantly share code, notes, and snippets.

@rileyjshaw
Created May 19, 2014 22:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rileyjshaw/1e49ed14046d31c00b63 to your computer and use it in GitHub Desktop.
Save rileyjshaw/1e49ed14046d31c00b63 to your computer and use it in GitHub Desktop.
A Pen by Riley Shaw.

Glorious Pattern Matcher

Takes a series of characters ('abcad') and a series of space-delimited words ('red blue green red redux') and checks if they match the same sequence.

Mega-Glorious Pattern Matcher Takes a series of characters ('abcad') and a series of non-space-delimited words ('redbluegreenredredux') and checks if they match the same sequence. Hardcore.

Updated version here, on GitHub here.

A Pen by Riley Shaw on CodePen.

License.

<h1>Glorious Pattern Matcher</h1>
<p>In the left box, type a series of characters representing an ordered pattern. In the right box, type a series of words separated by spaces. The <strong>⚙</strong> button will let you know if the words match the pattern specified in the first box.</p>
<h2 id="exampleToggle1">Examples</h2>
<div id="exampleContent1" class="drawer">
<div>
<p class="box">AAA</p>
<p class="box">Ho Ho Ho</p>
<p class="match">Match</p>
</div>
<div>
<p class="box">AAA</p>
<p class="box">Oh No No</p>
<p class="nomatch">No match</p>
</div>
<div>
<p class="box">ABBA</p>
<p class="box">Knowing Me Knowing You</p>
<p class="nomatch">No match</p>
</div>
<div>
<p class="box">ABBA</p>
<p class="box">Knowing Me Me Knowing</p>
<p class="match">Match</p>
</div>
</div>
<form id="test1" class="clearfix" autocomplete="off">
<input type="text" id="pattern1">
<input type="text" id="data1">
<button type="submit"><span>⚙</span></button>
</form>
<p id="result1"></p>
<h1>Mega-Glorious Pattern Matcher</h1>
<p>Same drill as before, but this time omit the spaces in the right box. Hardcore.</p>
<h2 id="exampleToggle2">Examples</h2>
<div id="exampleContent2" class="drawer">
<div>
<p class="box">hehe</p>
<p class="box">redrumredrum</p>
<p class="match">Match</p>
</div>
<div>
<p class="box">jack</p>
<p class="box">redrumredrum</p>
<p class="nomatch">No match</p>
</div>
<div>
<p class="box">ACDC</p>
<p class="box">I'mT.N.T.</p>
<p class="match">Match</p>
</div>
</div>
<form id="test2" class="clearfix" autocomplete="off">
<input type="text" id="pattern2">
<input type="text" id="data2">
<button type="submit"><span>⚙</span></button>
</form>
<p id="result2"></p>
<small>by <a href="http://rileyjshaw.com">rileyjshaw</a></small>
var glorious = (function() {
var exports = {};
// takes a character pattern and a list of words
// and returns true if they follow the same pattern
exports.match = function(pattern, data) {
var patternLength = pattern.length;
var matches = {}, matchValues = {};
var curPattern, curData;
// handles arrays and strings
if (typeof pattern === 'string') {
pattern = pattern.split('');
}
// handles arrays and space-delimited strings
if (typeof data === 'string') {
data = data.split(' ');
}
if (patternLength !== data.length) {
return false;
}
for (var i = 0; i < patternLength; i++) {
curPattern = pattern[i];
curData = data[i];
if (!matches[curPattern]) {
if (!matchValues[curData]) {
matches[curPattern] = curData;
matchValues[curData] = true;
} else return false;
} else { // already recorded
if (matches[curPattern] !== curData) {
return false;
}
}
}
return true;
};
// takes a string and returns an array of substrings.
// splits are determined by the 'lengths' array
function substringify(lengths, string) {
var i = 0;
return lengths.map(function(len) {
var substring = string.substr(i, len);
i += len;
return substring;
});
}
// finds likely substrings from non-delimited data
// and runs them through the original match()
function tryPossibleConfigurations(coefficients, patternLength, dataLength, func) {
function weightedAdd(accum, cur, i) {
return accum + cur * lengths[i];
}
// lengths holds the substring lengths represented by each character
var lengths = [];
var totalDepth = coefficients.length;
// each level of the recursive call represents a unique
// character in the pattern (in the order that they first appear)
function loopThroughChars(depth) {
var isInnermost = (depth === totalDepth - 1);
var curCharCount = coefficients[depth];
var derivedDataLength;
// maxCharLength is the max substring length that this character can represent
var maxCharLength = (dataLength - patternLength + curCharCount) / curCharCount;
for(var i = 1; i <= maxCharLength; i++) {
lengths[depth] = i;
if (!isInnermost) {
if (loopThroughChars(depth + 1)) {
return true; // allows us to bubble up a true return
}
} else {
derivedDataLength = coefficients.reduce(weightedAdd, 0);
if (derivedDataLength < dataLength) continue;
else if (derivedDataLength === dataLength) {
if (func(lengths)) {
return true; // break out of all loops (bubbles up)
}
}
else {
return false; // breaks out of this loop (no bubbling)
}
}
}
return false;
}
return loopThroughChars(0);
}
exports.megamatch = function(pattern, data) {
var splitPattern = pattern.split('');
var patternLength = splitPattern.length;
var dataLength = data.length;
// coefficients stores the count of each character
// charIdx maps a character to its index in coefficients
var coefficients = [], charIdx = {};
// populate coefficients and charIdx
splitPattern.forEach(function(char) {
if (charIdx[char] === undefined) {
charIdx[char] = coefficients.push(1) - 1;
} else ++coefficients[ charIdx[char] ];
});
function splitAndMatch(lengths) {
var substrings = splitPattern.map(function(char) {
return lengths[ charIdx[char] ];
});
var splitData = substringify(substrings, data);
return exports.match(splitPattern, splitData);
}
return tryPossibleConfigurations(coefficients, patternLength, dataLength, splitAndMatch);
};
return exports;
})();
(function(document) {
var domNodes = {
exampleToggle1: document.getElementById('exampleToggle1'),
exampleToggle2: document.getElementById('exampleToggle2'),
exampleContent1: document.getElementById('exampleContent1'),
exampleContent2: document.getElementById('exampleContent2'),
test1: document.getElementById('test1'),
test2: document.getElementById('test2'),
pattern1: document.getElementById('pattern1'),
pattern2: document.getElementById('pattern2'),
data1: document.getElementById('data1'),
data2: document.getElementById('data2'),
result1: document.getElementById('result1'),
result2: document.getElementById('result2')
};
function toggleOpen() {
this.className = this.className ? '' : 'open';
}
domNodes.exampleToggle1.addEventListener('click', toggleOpen);
domNodes.exampleToggle2.addEventListener('click', toggleOpen);
domNodes.test1.addEventListener('submit', function(event) {
var pattern = domNodes.pattern1.value;
var data = domNodes.data1.value;
event.preventDefault();
if (glorious.match(pattern, data)) {
domNodes.result1.textContent = 'Match';
domNodes.result1.className = 'match';
} else {
domNodes.result1.textContent = 'No match';
domNodes.result1.className = 'nomatch';
}
return false;
});
domNodes.test2.addEventListener('submit', function(event) {
var pattern = domNodes.pattern2.value;
var data = domNodes.data2.value;
event.preventDefault();
if (glorious.megamatch(pattern, data)) {
domNodes.result2.textContent = 'Match';
domNodes.result2.className = 'match';
} else {
domNodes.result2.textContent = 'No match';
domNodes.result2.className = 'nomatch';
}
return false;
});
})(document);
@import "compass/css3"
$clight: #fff
$cdark: #443b33
$cblue: #88bbee
body
width: 61.803398875%
max-width: 820px
margin: auto
font-family: Droid Sans, sans-serif
background-color: $clight
body, input
color: $cdark
h1, h2, p, input, button
margin-top: 1em
line-height: 1.61803398875em
h1
font-size: 62px
text-shadow: 0 6px $clight - #222, 0 12px $clight - #0e0e0e
h2, a
color: $cblue
h2
font-size: 15px
cursor: pointer
&:after
content: '▼'
position: relative
top: -2px
display: inline-block
margin-left: 1em
font-size: 9px
-moz-transition: -moz-transform 0.2s
-o-transition: -o-transform 0.2s
-webkit-transition: -webkit-transform 0.2s
transition: transform 0.2s
&.open:after
-moz-transform: rotate(180deg)
-ms-transform: rotate(180deg)
-o-transform: rotate(180deg)
-webkit-transform: rotate(180deg)
transform: rotate(180deg)
p, small
font-size: 24px
input, button
float: left
height: 1.61803398875em
line-height: 1em
box-sizing: border-box
border: 0
outline: 0
font-family: Droid Sans, sans-serif
font-size: 38px
input
width: 35%
margin-right: 8%
border-top: 8px solid $cdark + #222
color: $clight - #222
background-color: $cdark
button
width: 14%
color: $clight
background-color: $cblue
cursor: pointer
span
display: inline-block
padding-bottom: 3px
&:hover span
-moz-animation: spin 2s infinite linear
-o-animation: spin 2s infinite linear
-webkit-animation: spin 2s infinite linear
animation: spin 2s infinite linear
small
display: block
margin: 3em 0 4em
a
text-decoration: none
#result1, #result2
max-height: 0
position: relative
left: -100%
overflow: hidden
-moz-transition: max-height 0.2s, left 0.2s 0.4s
-o-transition: max-height 0.2s, left 0.2s 0.4s
-webkit-transition: max-height 0.2s, left 0.2s 0.4s
transition: max-height 0.2s, left 0.2s 0.4s
&.match, &.nomatch
max-height: 2em
left: 0
.match
color: #66ee99
.nomatch
color: #ee88bb
.drawer
overflow: hidden
max-height: 0
-moz-transition: max-height 0.8s
-o-transition: max-height 0.8s
-webkit-transition: max-height 0.8s
transition: max-height 0.8s
.open + &
max-height: 1000px
p
display: inline-block
padding: .24em .61em
.box
position: relative
margin-right: 1em
color: $clight
background-color: $cdark + #444
div:nth-of-type(1) p
&:before
position: absolute
top: -28px
left: 0px
font-size: 12px
font-weight: bold
color: #888
&:nth-of-type(1):before
content: 'Box 1'
&:nth-of-type(2):before
content: 'Box 2'
// lol
.clearfix
&:before, &:after
content: " "
display: table
&:after
clear: both
@-webkit-keyframes spin
0%
-webkit-transform: rotate(0deg)
100%
-webkit-transform: rotate(359deg)
@keyframes spin
0%
transform: rotate(0deg)
100%
transform: rotate(359deg)
@media (max-width: 900px)
body
width: 90.4508497187%
@media (max-width: 720px)
.drawer
div
margin-bottom: 38px
p
display: block
&.match, &.nomatch
margin-top: -0.5em
padding: 0
&:before
position: absolute
top: -28px
left: 0px
font-size: 12px
font-weight: bold
color: #888
&:nth-of-type(1):before
content: 'Box 1'
&:nth-of-type(2):before
content: 'Box 2'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment