text string to matrix in Javascript - javascript

Beginner here. I've got a text area. I want to break the text up into a matrix and then transpose rows and columns. I've got function that splits the text into groups. another function that puts them into an array and that's as far as I've got. I don't really know how to combine them into one function or if that is needed. i believe i'm supposed convert the array into a matrix by creating sub arrays, then i can finally transpose the text from [ i ][ j ] to [ j ][ i ]. Is this right so far or have i left something out? I'd love to see a working example someplace but can't seem to find one.
function G_Group(size, count)
{
if (size <= 0)
{
size = document.encoder.group_size.value;
if (size <= 0)
{
alert('Invalid group size');
return false;
}
}
if (count <= 0)
{
count = document.encoder.group_count.value;
if (count <= 0)
{
alert('Invalid group count');
return false;
}
}
var t = document.encoder.text.value;
var o = '', groups = 0;
t = Tr(t, " \r\n\t");
while (t.length > 0)
{
if (o.length > 0)
{
o += ' ';
}
if (groups >= count)
{
o += "\n";
groups = 0;
}
groups ++;
o += t.slice(0, size);
t = t.slice(size, t.length);
}
document.encoder.text.value = o;
return false;
}
function toArray()
{
var str = document.encoder.text.value;
var nstr = str.split(" ");
document.encoder.text.value = nstr;
}
//i am having trouble with this part. don't know how to call the document.encoder.text.value into the list and don't know how to call group_size into elementsPerSubArray
function toMatrix(list, elementsPerSubArray) {
var matrix = [], i, k;
for (i = 0, k = -1; i < list.length; i++) {
if (i % elementsPerSubArray === 0) {
k++;
matrix[k] = [];
}
matrix[k].push(list[i]);
}
return matrix;
}
Finally, i don't know how to post result back into the textarea
So far i can group the text into block of how many characters i choose from a selection box (i've got 30 options) and can make line breaks wherever i want. as far as the matrix goes, line breaks will be made after 1 group of (X) characters.
Given this text:
OOMUCHABOUTMYCOUNTRYICAREALOTH
I can break it into groups like this.
OOMUC HABOU TMYCO UNTRY ICARE ALOTH
or like this if i want.
OOMUC
HABOU
TMYCO
UNTRY
ICARE
ALOTH
What i want to do is be able to flip the matrix so it looks like this
AIUTHO
LCNMAO
...ect
HEYOUC

Related

How do I speed up finding shortest path with unique combinations of the 2 dimensions?

Long story short, I'm making a search box that has an array representing the distance between the search words and the words in the results. After these distances are calculated, I have a data structure like dataset below. The first dimension represents what resultWord I'm dealing with whereas the index on the second dimension represents what searchWord I'm dealing with.
The Goal: Only match a search word to a result word once. If a result word was used as a match already, it cannot be reused. And the resulting path should be the one in which the distance when added up between all the combinations is the lowest.
After I type about three search words, Chrome starts to freeze up. Chrome's profiler says it's definitely coming from getBestDistancePath. And I don't understand why, it's only comparing integers. It's not like it's doing string manipulation or anything.
Is there anyway to speed this up with caching or a completely different way to go about it that would be faster?
If I entered "m g" into the search box this is basically what it would translate into for the row that had a column "Medical: Gastrointestinal Assessment":
var dataset = [
[
{
"distance":7,
"searchWord":"m",
"resultWord":"medical:"
},
{
"distance":8,
"searchWord":"g",
"resultWord":"medical:"
}
],
[
{
"distance":16,
"searchWord":"m",
"resultWord":"gastrointestinal"
},
{
"distance":15,
"searchWord":"g",
"resultWord":"gastrointestinal"
}
],
[
{
"distance":9,
"searchWord":"m",
"resultWord":"assessment"
},
{
"distance":10,
"searchWord":"g",
"resultWord":"assessment"
}
]
];
// Find best unique searchWord to resultWord match up for lowest edit distance
let path = getBestDistancePath(dataset, []);
// Convert path into more usable representation
let result = path.map(point => {
return dataset[point[0]][point[1]];
});
document.getElementById('output').innerHTML = JSON.stringify(result, null, 2);
function didUseIndex(path, index, value) {
for (var ii = 0; ii < path.length; ii++) {
if (path[ii][index] === value) {
return true;
}
}
return false;
}
function getBestDistancePath(distances, currentPath) {
let distanceLen = distances.length;
if (distanceLen === 0)
return currentPath;
// Only need the best combos for the amount of search words that exist
let searchWordLen = distances[0].length;
if (currentPath.length == searchWordLen)
return currentPath;
let min = Infinity;
let bestPath = currentPath;
for (let i = 0; i < distanceLen; i++) {
// i tells me what resultWord is being used
if (didUseIndex(currentPath, 0, i)) {
continue;
}
for (let j = 0; j < searchWordLen; j++) {
// j tells me what searchWord is being used
if (didUseIndex(currentPath, 1, j)) {
// If this version has been used in this trial, skip it
continue;
}
// Concat creates new array so it isn't shared memory
let path = getBestDistancePath(distances, currentPath.concat([ [i, j] ]));
// Add up all the distances in the path
let totalDistance = 0;
for (let k = 0, len3 = path.length; k < len3; k++) {
let curr = path[k];
totalDistance += distances[curr[0]][curr[1]].distance;
}
if (totalDistance < min) {
min = totalDistance;
bestPath = path;
}
}
}
return bestPath;
}
<pre id="output"></pre>

I am trying to loop through an array of all the alphabets and capitalize every other alphabet. any solutions?

this is the code i came up with:
var alpha = "abcdefghijklmnopqrstuvwxyz".split('');
// console.log(alpha);
// console.log(alpha.length);
for(i=0; i < alpha.length + 1; i++){
if (alpha.indexOf('a', +1) % 2 === 0){
console.log(indexOf('a'));
} else {
console.log("didn't work");
}
};
A simple loop with a step:
for (var i = 0; i < alpha.length; i+=2) {
alpha[i] = alpha[i].toUpperCase();
}
alpha.join(''); // AbCdEfGhIjKlMnOpQrStUvWxYz
If aBcDeFgHiJkLmNoPqRsTuVwXyZ is what you want to achieve, than you can do something like this:
var alpha = 'abcdefghijklmnopqrstuvwxyz';
var result = '';
for (var i = 0; i < alpha.length; i++) {
if ((i + 1) % 2 === 0){
result += alpha[i].toUpperCase();
} else {
result += alpha[i];
}
}
console.log(result);
You can map your array and upper case every second character:
var alpha = "abcdefghijklmnopqrstuvwxyz".split('').map(function(ch, i) {
return i % 2 ? ch.toUpperCase() : ch;
});
console.log(alpha);
The issue with strings is that you can't edit them, once created they stay the same. Most actions on them create a new string.
To avoid doing this lots of times, we do the following
var alpha = 'abcdefghijklmnopqrstuvwxyz'.split('');
Convert the string into an an array
for (var i = 0; i< alpha.length; i++) {
if(i % 2 == 0) {
go down the array, and for every other entry (i % 2 gives us 0 every other time).
alpha[i] = alpha[i].toUpperCase();
convert it to upper case
}
}
var newString = alpha.join('');
and finally make a new string by joining all the array elements together. We have to provide a null string ie '' because if we didn't provide anything we would join with commas (,)
var alpha = "abcdefghijklmnopqrstuvwxyz".split('');
for(i=0; i < alpha.length; i++){
console.log(alpha[i].toUpperCase());
//If you want to update
alpha[i] = alpha[i].toUpperCase();
};

Highlight textarea on the fly

I'm looking to highlight text in a textarea by specifying a background color and a range of indexes. I'll then be constantly changing and updating the highlighted text dynamically as the user types.
jquery.highlightTextarea is almost perfect. The problem is that it appears that it wasn't really designed to update the highlighted text on the fly like this. And in addition to that, the author has now discontinued it and won't be updating it anymore.
I even went so far as to look at Ace Code Editor, but the ability to highlight based on simple indexes wasn't really there with that one (you have to highlight based on the rows/columns). Plus it seemed like it was overkill for what I'm doing.
So I'm looking for alternatives. In addition to the features I mentioned, performance is high on my list of priorities as well. I'd like to compile a list of libraries to consider. Any suggestions?
I finally figured something out that's working well for me. I'm using the Ace Editor. I went back and forth between Ace and CodeMirror, and finally settled on Ace because I felt like this particular task was a little more straightforward and the documentation is better too. I say straightforward, but the complication is that I have to convert index ranges into column / row ranges because that's the format the Range object in Ace is expecting.
But here's what I came up with:
function HighlightMatches(matches) {
var matchClass = "myMarker";
var oldMarkers = inputEditor.session.$backMarkers;
// clear out old markers
for (var prop in oldMarkers) {
if (oldMarkers[prop].clazz == matchClass) {
inputEditor.session.removeMarker(oldMarkers[prop].id);
}
}
if (matches && matches.length > 0) {
var ranges = NormalizedRanges(GetInput(), matches);
// add new markers
for (var i = 0; i < ranges.length; i++) {
inputEditor.session.addMarker(new Range(ranges[i].startRow, ranges[i].startCol, ranges[i].endRow, ranges[i].endCol), matchClass);
}
}
}
function NormalizedRanges(input, matches) {
var ranges = [];
var startCol = 0;
var endCol = 0;
var startRow = 0;
var endRow = 0;
var currentMatch = 0;
var matchStart = 0;
var matchLength = 0;
var matchEnd = 0;
for (var i = 0; i < input.length; i++) {
matchStart = matches[currentMatch].index;
matchLength = matches[currentMatch].length;
matchEnd = matchStart + matchLength;
// process match
if (i >= matchStart && i <= matchEnd) {
endCol = startCol;
endRow = startRow;
for (var k = 0; k < matchLength; k++) {
endCol++;
var advanceNewLine = AdvanceNewLine(input.substr(i + k, 2))
if (advanceNewLine > 0) {
endRow++;
endCol = 0;
if (advanceNewLine == 2)
k++;
}
}
ranges.push(new IndexRange(startCol, endCol, startRow, endRow));
startCol = endCol;
startRow = endRow;
// set index to end of match
i = matchEnd - 1;
// advance current match
currentMatch++;
if (currentMatch == matches.length)
return ranges;
}
else {
// advance range
startCol++;
var advanceNewLine = AdvanceNewLine(input.substr(i, 2));
if (advanceNewLine > 0) {
startRow++;
startCol = 0;
if (advanceNewLine == 2)
i++;
}
}
}
return ranges;
}
function AdvanceNewLine(text) {
if (text == '\r\n')
return 2;
else {
var char = text.charAt(0);
if (char == '\n' || text == '\r')
return 1;
else
return 0;
}
}
function IndexRange(startCol, endCol, startRow, endRow) {
this.startCol = startCol;
this.endCol = endCol;
this.startRow = startRow;
this.endRow = endRow;
}
You simply call HighlightMatches and pass in a matches JSON object that might look something like this:
{"matches":[{"index":0,"length":1},{"index":4,"length":1}]}

JavaScript procedure to find the characters that are in one string but not in another

I'e been trying to write one and it's getting messy!
Suppose I have two strings textStart, textTarget and I want to keep track of the characters I would need to add and remove from textStart in order to product textTarget.
For instance, if textStart = "dude" and textTarget = "deck", then characters that would need to be added would be 'c' and 'k' and the characters that would need to be substracted would be the 'u' and one of the 'd's.
I'm thinking that I first need to create maps that represent the number of each character in textStart and textTarget.
So I wrote this:
var startChars = {};
for (var k = 0, n = textStart.length; k < n; ++k)
{
if (textStart[k] in startChars)
++startChars[textStart[k]];
else
startChars[textStart[k]] = 1;
}
var targetChars = {};
for (var k = 0, n = textTarget.length; k < n; ++k)
{
if (textTarget[k] in startChars)
++targetChars[textTarget[k]];
else
map1[targetChars[k]] = 1;
}
Which would give me
startChars['d']=2,
startChars['u']=1,
startChars['e']=1
and
targetChars['d']=1,
targetChars['e']=1,
targetChars['c']=1,
targetChars['k']=1
Then I can make create maps needAdded and needRemoved that look at the difference in the above two maps:
var needAdded = {};
var needRemoved = {};
I'm not sure how to fill those maps as intended, because I don't know how to iterate through the keys of a map using JavaScript. I somehow need to end up with
needAdded['c']=1,
needAdded['k']=1,
needRemoved['u']=1,
needRemoved['d']=1
That's where you guys come in and help me.
I hope I've done a good job describing what I'm trying to do and how I've tried to do it so far. My programming intuition tells me that I'm writing too many lines of code and that I need to consult StackOverflow for help. Any way to do this elegantly without JQuery or Regex? I know someone's going to come in this thread and write a 1-line Regex solution or something like that.
var s = 'dude',
t = 'deck',
finalOutput = '';
for (var i = 0; i < s.length; i++){
if ( typeof t[i] != 'undefined' ){
if ( s[i] != t[i] ){
console.log(s[i] + ' changed to ' + t[i]);
s[i] = t[i];
finalOutput += t[i];
} else{
finalOutput += s[i];
}
}
}
console.log('FINAL: ' + finalOutput);
Here's a jsfiddle I just spent way too much time on... hopefully it makes sense :)
var textStart = 'dude';
var textTarget = 'deck';
var startChars = {};
for (var k = 0, n = textStart.length; k < n; ++k)
{
if (textStart[k] in startChars)
++startChars[textStart[k]];
else
startChars[textStart[k]] = 1;
}
var targetChars = {};
for (var k = 0, n = textTarget.length; k < n; ++k)
{
if (textTarget[k] in targetChars)
++targetChars[textTarget[k]];
else
targetChars[textTarget[k]] = 1;
}
console.log('start: ' + JSON.stringify(startChars));
console.log('target: ' + JSON.stringify(targetChars));
var needAdded = {};
var needRemoved = {};
for (var c in startChars) {
// If target does not contain letter, remove all, otherwise remove excess
if (targetChars[c] > 0) {
if (startChars[c] > targetChars[c])
needRemoved[c] = startChars[c] - targetChars[c];
else if (startChars[c] < targetChars[c])
needAdded[c] = targetChars[c] - startChars[c];
} else {
needRemoved[c] = startChars[c];
}
}
for (var c in targetChars) {
// If start does not contain letter, add all, otherwise add excess
if (startChars[c] > 0) {
if (startChars[c] > targetChars[c])
needRemoved[c] = startChars[c] - targetChars[c];
else if (startChars[c] < targetChars[c])
needAdded[c] = targetChars[c] - startChars[c];
} else {
needAdded[c] = targetChars[c];
}
}
console.log('needAdded: ' + JSON.stringify(needAdded));
console.log('needRemoved: ' + JSON.stringify(needRemoved));
The output is as follows:
start: {"d":2,"u":1,"e":1}
target: {"d":1,"e":1,"c":1,"k":1}
needAdded: {"c":1,"k":1}
needRemoved: {"d":1,"u":1}
Ok, also too much time on this:
var textStart = "dude";
var textTarget = "duck";
var map = {};
MapCharacters(textStart, map, 1);
MapCharacters(textTarget, map, -1);
console.log(map);
var toDelete = [];
var toAdd = [];
for (var prop in map) {
if (map.hasOwnProperty(prop)) {
while (map[prop] > 0) {
toDelete.push(prop);
map[prop]--;
}
while (map[prop] < 0) {
toAdd.push(prop);
map[prop]++;
}
}
}
console.log(toDelete);
console.log(toAdd);
function MapCharacters(string, map, add) {
for (var k = 0, n = string.length; k < n; ++k) {
if (string[k] in map) {
map[string[k]] += add;
} else {
map[string[k]] = add;
}
}
}
http://jsfiddle.net/nSV2J/1/
It could probably be done more efficiently, but as I said - too much time!
I realized that the best way to do this is not to make two maps, but just one. In the first case you increment the count for each letter and in the second case you decrease it. Now it's easy to find which ones need to be removed (the ones that end up > 0) and which ones need to be added (the ones that end up < 0)

Javascript string matching pattern help

i need to find few words or matching pattern using a Javascript.
this is the requirement.
i have a string like this,
Here is a quick guide for the next
time you reach for your favorite oil and some other topics
and i need to match this string against a string like this
favorite oil and some other topics can be based on something blah blah
how do i get the intersection of matching text blocks?
I already tried intersect Javascript script function, for some strings it's not working properly.
How to solve this problem? can this be done using Regex?
Please advice.
You have to find the Longest common substring.
If the strings are not very long, I recommend using Tim's approach. Otherwise, this is a Javascript implementation of the Longest common substring algorithm with dynamic programming. The runtime is O(mn) where m and n are the lengths of the 2 strings respectively.
An example usage:
var first = "Here is a quick guide for the next time you reach for your favorite oil and some other topics";
var second = "favorite oil and some other topics can be based on something blah blah";
console.log(first.intersection(second)); // ["favorite oil and some other topic"]
This is the algorithm implementation. It returns an array of the longest common substring(s). Extended the native String class, so the intersect method is available on all strings.
String.prototype.intersection = function(anotherString) {
var grid = createGrid(this.length, anotherString.length);
var longestSoFar = 0;
var matches = [];
for(var i = 0; i < this.length; i++) {
for(var j = 0; j < anotherString.length; j++) {
if(this.charAt(i) == anotherString.charAt(j)) {
if(i == 0 || j == 0) {
grid[i][j] = 1;
}
else {
grid[i][j] = grid[i-1][j-1] + 1;
}
if(grid[i][j] > longestSoFar) {
longestSoFar = grid[i][j];
matches = [];
}
if(grid[i][j] == longestSoFar) {
var match = this.substring(i - longestSoFar + 1, i);
matches.push(match);
}
}
}
}
return matches;
}
Also need this helper function to create a 2d array with all elements initialize to 0.
// create a 2d array
function createGrid(rows, columns) {
var grid = new Array(rows);
for(var i = 0; i < rows; i++) {
grid[i] = new Array(columns);
for(var j = 0; j < columns; j++) {
grid[i][j] = 0;
}
}
return grid;
}
This isn't very efficient and there are much better ways to do this in general (see #Anurag's answer), but it's simple and works fine for short strings:
function stringIntersection(str1, str2) {
var strTemp;
// Swap parameters if necessary to ensure str1 is the shorter
if (str1.length > str2.length) {
strTemp = str1;
str1 = str2;
str2 = strTemp;
}
// Start with the whole of str1 and try shorter substrings until
// we have a common one
var str1Len = str1.length, l = str1Len, start, substring;
while (l > 0) {
start = str1Len - l;
while (start >= 0) {
substring = str1.slice(start, l);
if (str2.indexOf(substring) > -1) {
return substring;
}
start--;
}
l--;
}
return "";
}
var s1 = "Here is a quick guide for the next time you reach"
+ " for your favorite oil and some other topics";
var s2 = "favorite oil and some other topics can be based on"
+ " something blah blah";
alert( stringIntersection(s1, s2) );
A simple polyfill of filter a string
if (!String.prototype.intersection) {
String.prototype.intersection = function(anotherString, caseInsensitive = false) {
const value = (caseInsensitive) ? this.toLowerCase() : this;
const comp = (caseInsensitive) ? anotherString.toLowerCase() : anotherString;
const ruleArray = comp.split("").reduce((m,v) => {m[v]=true; return m;} ,{})
return this.split("").filter( (c, i) => ruleArray[value[i]] ).join("")
}
}
"HelloWorld".intersection("HEWOLRLLODo", true)
"HelloWorld" - case insensitive
"HelloWorld".intersection("HEWOLRLLODo")
"HoWo" - case sensitive

Categories