textarea duplicate string check ignoring leading and trailing whitespace - javascript

Users will enter various serials in a textarea. Each newline will indicate a new serial. Some requirements/restrictions:
Leading and trailing white spaces are not allowed.
White space within a serial is okay.
Blank serials are not allowed
I'd prefer to not use JQuery.
Store duplicates so they can be shown to the user.
Based on my tests I have a working solution. I want to make sure I'm not missing or overlooking anything. My questions are:
Is there a more efficient ways to check for duplicates?
Are there any glaring test cases that my solution won't catch?
Working Example: http://jsbin.com/ivusuj/1/
function duplicateCheck() {
var output = document.getElementById('Output');
output.innerHTML = '';
var duplicateSerials = [];
var count = 0;
var textArea = document.getElementById('Serials');
var serials = textArea.value.trim().split(/ *\n */);
for(var i = 0;i < serials.length;i++){
var serial = serials[i];
if(serials.indexOf(serial) != serials.lastIndexOf(serial) &&
duplicateSerials.indexOf(serial) == -1 && serial !== '') {
duplicateSerials.push(serial);
}
}
// For testing
output.innerHTML = '<pre>Serials:\t' + serials.toString() + "<br />" +
'Duplicates:\t' + duplicateSerials.toString() + "<br>" +
'</pre>';
}
Note: the above is for a client side check. The same check will be performed server side as well to ensure the data is valid.
Update
Solution comparison: http://jsbin.com/ivusuj/4/edit

I put together a jsfiddle her: http://jsfiddle.net/wrexroad/yFJjR/3/
Actually checking for duplicates that way is pretty inefficient.
Instead of checking for duplicates, this just adds a property to an object where the property's name is is the serial. Then it prints out all of the property names.
This way if you have duplicates, it will just create the property, then overwrite it.
Here is the function:
function duplicateCheck() {
var output = document.getElementById('Output');
output.innerHTML = '';
var textArea = document.getElementById('Serials');
var inputSerials =
textArea.value.trim().split(/ *\n */);
var outputSerials = new Object();
for(var i = 0;i < inputSerials.length;i++){
var serial = inputSerials[i];
//build an object whose properties are serials
//if the serial exists, incremint a counter
if(outputSerials[serial]){
outputSerials[serial]++;
}else{
outputSerials[serial] = 1;
}
}
output.innerHTML =
'Serials: <br />';
for(var i in outputSerials){
output.innerHTML += i + " ";
}
output.innerHTML +=
'<br /><br />Duplicate Serials: <br />';
for(var i in outputSerials){
//check to see if we have any duplicates
if(outputSerials[i] > 1){
output.innerHTML += i + " ";
}
}
}

I think you'd get significantly better performance if you used an object to determine which serials you'd seen before. Something closer to this:
var seen = {};
for (var i = 0, j = serials.length; i < j; ++i) {
var serial = serials[i];
if (seen.hasOwnProperty(serial)) {
// Dupe code goes here.
continue;
}
// Can't be a duplicate if we get to this point.
}
Though that won't work with serials that use periods.

Here's a solution to filter out duplicates.
function formatInput() {
var arrUnique = [], dups = [],
str = document.getElementById('Serials').value
.replace(/\r\n?/g,'\n')
// normalize newlines - not sure what PC's
// return. Mac's are using \n's
.replace(/(^((?!\n)\s)+|((?!\n)\s)+$)/gm,'')
// trim each line
.replace(/^\n+|\n+$|\n+(?=\n(?!\n))/g,''),
// delete empty lines and trim the whole string
arr = str.length ? str.split(/\n/) : [];
// split each line, if any
for (var i = 0; i < arr.length; i++) {
if (arrUnique.indexOf(arr[i]) == -1)
arrUnique.push(arr[i]);
else dups.push(arr[i]);
}
//document.getElementById('Serials').value = arrUnique.join('\n');
console.log('serials:', arr);
console.log('unique:', arrUnique);
console.log('duplicates:', dups);
}

Related

Javascript Markdown Parsing

I'm working on a markdown to html parser. I understand this is a big project and there are third party libraries, but none the less I want to roll a simple solution on my own that doesn't have to handle every single aspect of markdown.
So far the process is to take an input (in my case the value of a textarea) and parse it line by line.
var html = [];
var lines = txt.split('\n'); //Convert string to array
//Remove empty lines
for(var index = lines.length-1; index >= 0; index--) {
if(lines[index] == '') lines.splice(index, 1);
}
//Parse line by line
for(var index = 0; index <= lines.length-1; index++) {
var str = lines[index];
if(str.match(/^#[^#]/)) {
//Header
str = str.replace(/#(.*?)$/g, '<h1>$1</h1>');
} else if(str.match(/^##[^#]/)) {
//Header 2
str = str.replace(/##(.*?)$/g, '<h2>$1</h2>');
} else if(str.match(/^###[^#]/)) {
//Header 3
str = str.replace(/###(.*?)$/g, '<h3>$1</h3>');
} else if(str.trim().startsWith('+')) {
//Unordered List
var orig = str;
str = str.replace(/\+(.*?)$/, '<li>$1</li>');
var previous, next;
if(index > 0) previous = lines[index-1];
if(!previous || previous && previous.indexOf('+') < orig.indexOf('+')) {
str = '<ul>' + str;
}
if(index < lines.length-1) next = lines[index+1];
if(!next || next && next.indexOf('+') < orig.indexOf('+')) {
var count = Math.max(0, orig.indexOf('+') / 4);
if(next) count = count - Math.max(0, next.indexOf('+') / 4);
for(var i=1; i<=count; i++) {
str = str + '</ul>';
}
}
if(next && next.trim().indexOf('+') == -1) str = str + '</ul>';
} else if(str.match(/^[0-9a-zA-Z]/)) {
//Paragraph
str = str.replace(/^([0-9a-zA-Z].*?)$/g, '<p>$1</p>');
}
//Inline formatting
str = str.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); //Bold
str = str.replace(/\_\_(.*?)\_\_/g, '<strong>$1</strong>'); //Another bold
str = str.replace(/\*(.*?)\*/g, '<em>$1</em>'); //Italics
str = str.replace(/\_(.*?)\_/g, '<em>$1</em>'); //Another italics
//Append formatted to return string
html.push(str);
}
Where I run into problems is with nested blocks such as ul. Currently the code looks at a line that starts with a + and wraps it in an li. Great, but these list items never get placed within a ul. I could run through the output again after the line by line and just wrap every group of li's, but that screws me up when I have nested li's that require their own ul.
Any thoughts on how to apply these additional wrapper tags? I've considered using my own special characters around list type elements so I know where to add the wrapper tags, but that breaks traditional markdown. I wouldn't be able to pass the raw markdown to someone other than myself and know they'd understand what was going on.
Edit I updated my code sample to include a working sample. The working sample also supports nested lists.
You need a very simple state machine.
When you encounter the first + you add <ul> and raise a flag.
If you don't see a line that starts with + and your flag is raised, then close the </ul>

Get the next key from array from string with symbols

I'm working on a simple but difficult problem for me right now, I'm use to work in jQuery but need this to be done in Javascript.
So simple as it is, the user inputs a string lets say:
"hey, wanna hang today?". It should output the next character in my array, so it would be like this: "ifz, xboob iboh upebz?".
And I have tried everything I can come up with. Hopefully some of you guys see the problem right away.
I have set up a short jsFiddle that shows similar to what I got.
function gen() {
var str = document.getElementById('str').value,
output = document.getElementById('output');
var alph = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','æ','ø','å','a'];
for (var i=0;i<str.length;i++) {
var index = str[i].charAt(0),
e = alph.indexOf(index);
console.log(alph[e + 1]);
output.innerHTML += alph[e + 1];
}
}
If you only want to skip to next letter with those chars and leave the others like space and ? as they are:
var index = str[i].charAt(0),
e = alph.indexOf(index);
if(e == -1){
output.innerHTML += index;
}else{
output.innerHTML += alph[e + 1];
}
Update: using #David Thomas method, you could do the following: (wouldnt work for 'å' though)
var index= str[i].toLowerCase().charCodeAt(0);
if((index > 96 && index < 123)){ // a to z
output.innerHTML += String.fromCharCode(str[i].charCodeAt(0)+1);
}else{
output.innerHTML += str[i];
}
}
I'd personally recommend the following approach, which should work with any alphabet for which there's a Unicode representation and, somewhat importantly, doesn't require a hard-coded array of letters/punctuation for each language:
function gen() {
var str = document.getElementById('str').value,
strTo = '',
output = document.getElementById('output');
for (var i = 0; i < str.length; i++) {
strTo += String.fromCharCode(str[i].charCodeAt(0) + 1);
}
output.textContent = strTo;
}
// hey, wanna hang today? -> ifz-!xboob!iboh!upebz#
JS Fiddle demo.
References:
String.prototype.charCodeAt().
String.prototype.fromCharCode().
Why does gen(',') === 'a'?
var alph = 'abcdefghijklmnopqrstuvwxyz';
var e = alph.indexOf(',');
console.log(e);
// -1
console.log(alph[e + 1]);
// 'a'
You need to take this case into account; otherwise, any characters that aren't in alph will map to 'a'.
(I see that you've also duplicated 'a' at the start and end of alph. This works, though it's more common either to use the modulus operator % or to check explicitly if e === alph.length - 1.)
You just have to add an array with the non respected characters:
var ex = ['?','!',' ','%','$','&','/']
In whole
for (var i=0;i<str.length;i++) {
var index = str[i].charAt(0)
if (alph.indexOf(index) >-1) {
var e = alph.indexOf(index);
output.innerHTML += alph[e + 1];
} else {
var e = index;
output.innerHTML += e;
}
}
JSFIDDLE: http://jsfiddle.net/TRNCFRMCN/hs15f0kd/8/.

Javascript: using a for statement as a variable

I'm fairly new to javascript and something I've been playing with lately is the 'for' statement. I'm questioning one thing, though. I've learned how to make a 'for' statement do things as if it was an output, like this:
for (i = 0; i < 3; i++) {
console.log(i);
}
But what if you want to set a variable for the whole output of the 'for' statement?
var destinationArray = ["town", "areas", "bosses"];
var destinationArraySet = 1;
var i;
for ( i = 0; i < destinationArraySet; i++) {
console.log(destinationArray[i]);
} /*the whole thing should be equal to var destination */
var userDestinationPrompt = ("Where would you like to go? Available places: " +
/* var destination */
+
".").toUpperCase();
To give some more context: I'm making a game that allows further destinations when the destination before is cleared. Once that's achieved, I set destinationArraySet to a higher value, which means that more places would be logged and put after 'Available places'.
Help would be very appreciated! If there's something not clear enough let me know.
The for statement is not an expression, so it doesn't have a return value. Use a variable to collect values in the loop:
var destination = '';
for (var i = 0; i < destinationArraySet; i++) {
destination += destinationArray[i] + ' ';
}
Of course, if you only want to concatenate the values in part of an array, you can use the slice method to get part of it, then the join method:
var destination = destinationArray.slice(0, destinationArraySet).join(' ');
var destination = '';
var destinationArray = ["town", "areas", "bosses"];
var destinationArraySet = 1;
for (var i = 0; i < destinationArraySet; i++) {
destination += destinationArray[i] + '\n';
}
console.log(destination);
Try this -
var destinationArray = ["town", "areas", "bosses"];
var destinationArraySet = 1;
var i;
var availablePlaces = '';
var separator = '';
for ( i = 0; i < destinationArraySet; i++) {
availablePlaces += separator + destinationArray[i];
separator = ', ';
}
var userDestinationPrompt = ("Where would you like to go? Available places: " +
availablePlaces + ".").toUpperCase();
The for statement doesn't have an "output", it's not a function. Thinking for as a function will give you troubles later on. for is simply a statement that continuously execute the block of code inside. It does not "output", or in other words, return any value.
Do this instead:
var destinationArray = ["town", "areas", "bosses"], destinationArraySet = 1;
var userDestinationPrompt = ("Where would you like to go? Available places: " +
destinationArray.slice(0, destinationArraySet).join("\n")
+ ".").toUpperCase();
prompt(userDestinationPrompt);
Demo: http://jsfiddle.net/7c2b9q7m/1/
destinationArray.slice(0, destinationArraySet): Cuts the array to the specified length.
.join("\n"): Join the newly created array by \ns (newline) to micic the default console.log behavior.

Search Box Function Not Eliminating Correct Value

I am trying to make a simple website where the user types input into a search box, and every time a key is press, their input is compared against the first row of a 2 dimensional array which checks for character matches. If the character they input doesn't match anything, I want it to remove that specific bucket of the array. I have attempted to write basic code for this I thought would work, and have it up at the demo site linked. (Sorry I am just using a free host and havn't optimized the equation table at all so bear with it)
http://fakefakebuzz.0fees.net/
As you can see, the function is not eliminating the appropriate table rows. For example, typing "A" should not eliminate the "Average Current Equation" row because the first letter of that is A, which means matches should not = 0.
I have been looking through this code all morning, and cannot find where I went wrong. I also want to stick to vanilla js.
Any help?
Thanks so much.
I just debugged your code, and the function you use is narrowTable. first remove onkeypress from body node
<body onload="printTable()" onkeypress="narrowTable()">
and add onkeyup instead to you input, like this:
<input type="search" name="equationSearch" id="equationSearch"
placeholder="Equation Search" autofocus="" onkeyup="narrowTable()">
because when you use onkeypress the key value hasn't been added to the input box and your input value has no value in your function, which is:
function narrowTable() {
var newTableContent = "";
var matches = 0;
var input = document.getElementById("equationSearch").value;
//input has no value
for (var i = 0; i < tableData.length; i++) {
for (var j = 0; j < tableData[i][0].length; j++) {
if (input == tableData[i][0].charAt(j)) {
matches++;
}
}
if (matches == 0) {
tableData.splice(i, 1);
}
matches = 0;
}
for (var i = 0; i < tableData.length; i++) {
newTableContent += "<tr><td>" + tableData[i][0] + "</td><td>" + tableData[i][1] + "</td></tr>";
}
document.getElementById("table").innerHTML = newTableContent;
}
the other problem your code has is after printing your table, your tableData variable has changed because you have removed some of indexes. you should reset the tableData to its original value or you can do:
function narrowTable() {
//create a copy of your original array and use currenttableData instead
var currenttableData = tableData.slice();
var newTableContent = "";
var matches = 0;
//your code
}
the other problem here is the way you search for your input value:
for (var j = 0; j < tableData[i][0].length; j++) {
if (input == tableData[i][0].charAt(j)) {
matches++;
}
}
if (matches == 0) {
tableData.splice(i, 1);
}
you can easily do this, instead:
if(tableData[i][0].search("input") == -1){
tableData.splice(i, 1);
}
First, to check if a string is a substring of another string, you can use indexOf. It will return -1 if the string is not found in the other string.
Second, you shouldn't alter the array while you are still looping through it, unless you make sure to alter the counter variable (i in this case) appropriately.
var dataToRemove = [],
i;
for (i=0; i<tableData.length; i++) {
if(tableData[i][0].indexOf(input) == -1) {
// add the index to the to-be-removed array
dataToRemove.push(i);
}
// remove them in reverse order, so the indices don't get shifted as the array gets smaller
for(i = dataToRemove.length - 1; i >= 0; i--) {
tableData.splice(i, 1);
}
dataToRemove = [];
for (i=0; i<tableData.length; i++) {
newTableContent += "<tr><td>" + tableData[i][0] + "</td><td>" + tableData[i][1] + "</td></tr>";
}
I haven't tested this code, but it should at least give you a better idea of how to make this work.

Search Array in JavaScript

I need to sort through a data set which as you can see I've assigned to the records variable. From that data I need to see if the zip code exists. If the zip code does not exist then I need to move it into the array (There of course will be duplicates) and continue checking the rest of the records, if it does exist I need to do nothing.
// Declare Array
var numbersArray = [];
// Variables
var records;
var zipCode;
var numbers;
var index;
var output;
var outputMessageOne;
var outputMessageTwo;
var count = 0;
output = document.getElementById('outputDiv');
records = openZipCodeStudyRecordSet();
output.innerHTML = "The unique zip codes are: ";
while (records.readNextRecord()) {
zipCode = records.getSampleZipCode();
for (index = 0; index < numbersArray.length; index++) {
if (zipCode === numbersArray[index]) {
var uniqueZip = false;
break;
records++;
}
if (zipCode !== numbersArray[index]) {
numbersArray.push(zipCode);
}
}
output.innerHTML += numbersArray;
}
}
You can simplify your for loop like so:
matchedZip = false;
for(i in numbersArray) {
if (numbersArray[i] === zipCode) {
matchedZip = true;
}
}
if ( ! matchedZip) {
numbersArray.push(zipCode);
}
Try plugging that into your while loop. If you have the array push inside of the for loop you're going to end up pushing each zip code in every time there is not a match.
Well, you didn't exactly ask a question, but I'll answer anyway :) The answer is that you should not use a normal array for this, but rather a map or associative array. Fortunately a plain Javascript object can be used for this:
var numbers = {};
// Variables
var records;
var numbers;
var index;
var output;
var outputMessageOne;
var outputMessageTwo;
var count = 0;
output = document.getElementById('outputDiv');
records = openZipCodeStudyRecordSet();
output.innerHTML = "The unique zip codes are: ";
while (records.readNextRecord()) {
var zipCode = records.getSampleZipCode();
numbers[zipCode] = 1; // just picking an arbitrary value
}
for (var zipCode: numbers) {
output.innerHTML += zip + " ";
}
The reason is that this way you don't need to loop through the existing data for each new input.

Categories

Resources