Java Script problem with initial display of str.replaceAll [duplicate] - javascript

This question already has an answer here:
How to make Java Script ignore or maintain spaces, not delete them
(1 answer)
Closed 1 year ago.
I was given this code for a moving window self-paced reading experiment where the initial display is a sentence string covered by dashes (separated by word) and the display of regions (multiple words separated by "," in the csv) is iterated as you click through the sentence. The amount of time the participant spends in a region is recorded in the output csv. Below is the code and desired display:
var _pj;
function replaceWithdash(Sentence, currentWordNumber) {
const sentenceList = Sentence.split(",")
const sigil = sentenceList.map(s => s.replaceAll(/[^\s]/g, "-"))
if (currentWordNumber !== undefined) {
sigil.splice(currentWordNumber, 1, sentenceList[currentWordNumber])
}
return sigil.join("")
}
function _pj_snippets(container) {
function in_es6(left, right) {
if (((right instanceof Array) || ((typeof right) === "string"))) {
return (right.indexOf(left) > (- 1));
} else {
if (((right instanceof Map) || (right instanceof Set) || (right instanceof WeakMap) || (right instanceof WeakSet))) {
return right.has(left);
} else {
return (left in right);
}
}
}
container["in_es6"] = in_es6;
return container;
}
_pj = {};
_pj_snippets(_pj);
Trials_Display.text = replaceWithdash(Sentence, wordNumber);
keypresses = psychoJS.eventManager.getKeys();
sentenceList = Sentence.split(",");
if ((keypresses.length > 0)) {
if (_pj.in_es6("space", keypresses)) {
thisResponseTime = t;
wordNumber = (wordNumber + 1);
if ((wordNumber < sentenceList.length)) {
if ((wordNumber === 0)) {
timeOfLastResponse = 0;
}
thisExp.addData(("IRI_" + wordNumber.toString()), (thisResponseTime - timeOfLastResponse));
timeOfLastResponse = thisResponseTime;
Trials_Display.text = replaceWithdash(Sentence, wordNumber);
} else {
continueRoutine = false;
}
} else {
if (_pj.in_es6("escape", keypresses)) {
core.quit();
}
}
}
Desired Display:
str = "The dog, ate, the food"
console.log(replaceWithdash(str))
//"--- --- --- --- ----"
console.log(replaceWithdash(str, 0))
//"The dog --- --- ----"
console.log(replaceWithdash(str, 1))
//"--- --- ate --- ----"
console.log(replaceWithdash(str, 2))
// "--- --- --- the food"
Although this code does successfully show spaces between dashes and displays region by region, my problem is with the initial display of the dashed sentence. It looks like it's only showing the first two dashed regions and then once you click to reveal the words in the first region, it'll adjust to show the dashes for the third region. Also, there seems to be an extra space between regions for some reason. Something like this:
str = "The dog, ate, the food"
//"--- --- ---"
//"The dog --- --- ----"
//"--- --- ate --- ----"
// "--- --- --- the food"
I assume there's something wrong with how it's interpreting the initial string without an index (console.log(replaceWithdash(str)), but I'm not sure how to fix this, or the extra spaces.
I'm relatively new to Java script besides what I've taught myself, so any help would be appreciated!!

Wow, TLDR the comments. Here's how I'd do it:
function replaceWithDash(input,partNumber) {
//First, lets check if partNumber was omitted, since we can use this case
//to replace a single part as well
if(typeof partNumber!="number")
//Just remove the commas and replace every letter with the dash
return input.replace(/,/g,"").replace(/[^\s]/g,"-");
//If partNumber was specified (the function didn't end on the previous return), lets split the sentence
parts=input.split(",");
//remove the partNumber-th element and replace it with its dashed version
parts.splice(partNumber,1,replaceWithdash(parts[partNumber]));
//then join everything again and fix repeating spaces (it's quicker to do this once here than trimming each element)
return parts.join(" ").replace(/[\s]+/g," ");
}
Note that I fixed the function name.

Related

guessing words(like $|eeP!ng,#s,d0G) using node.js I tried to use object with aliases:{keys} saved in txt file but

My code will only guess fixed words ( less < 1000 )
I want to store all words/array/list/object - content in separate file (any better option except .txt)
I actually making a bot which reacts to fixed words only
I tried...
var word = '';
function guess(obj, arry) {
var keys = Object.keys(obj);
keys.forEach((key) => {
var inKey = key.split(/,\s?/);
inKey.forEach((l) => {
l == arry[0] && (word += inKey[0]) && guess(obj[key], arry.slice(1));
});
});
}
var guessLetters = {
"s, $, &, ʂ, ₰, ₷": {
"l, |, ł, ʟ": {
"e, ∃, ∊, £, €, ė, ɛ, ɘ , ə": {
"e, ∃, ∊, £, €, ė, ɛ, ɘ , ə": {
"p, ℗, ‽, ⁋, ₽, ₱, Þ, þ, ₽": "p"
}
}
}
},
"a, #": {
"s, $, &, ʂ, ₰, ₷": "s"
}
};
var guessThis = "₰ʟ£ɛ‽";
guess(guessLetters, guessThis.split(''));
console.log(word);
This solution I got after checking google and some related questions
problems...
repetition of same type of key like 's' for 'sleep' and 's' for 'as'
can't guess if 1 letter is missing in word
there is one more way I thought...
var translate = {
"#, ª, ₳":"a",
"₿, ฿":"b",
"₵, ₡, ¢":"c",
"£, €, ɛ":"e",
"₣":"f",
respetive to all 27 letters
};
but...
bot have to check the translated word in collection every time before reacting
ex. if I have CAT in my collection and user enter CAR then...
1st method will stop at R != T and ignore user
2nd method will translate whole CAR and also check (CAR in Collection) then ignore user

Can't get values past array[0] to translate properly

Okay, to start with I should mention this is a very small personal project, and I've only have a handful of coding classes several years ago now. I can figure out a lot of the (very) basics, but have a hard time troubleshooting. I'm in a little bit over my head here, and need a dumbed down solution.
I'm trying to put together a VERY simple translator that takes in a word or sentence from the user via a text input box, puts each word of the string into an array, translates each word in order, then spits out each translated word in the order it was input. For example, typing "I like cats" would output "Ich mag Katze" in German.
I've got most of it, but I CAN'T get anything but the first array element to translate. It comes out like "Ich like cats".
I've used a loop, probably because I'm an amateur and don't know another way of doing this, and I'd rather not use any libraries or anything. This is a very small project I want to have a couple of friends utilize locally; and I know there has to be some very simple code that will just take a string, put it into an array, swap one word for another word, and then output the results, but I'm damned if I can make it work.
What I currently have is the closest I've gotten, but like I said, it doesn't work. I've jerry-rigged the loop and clearly that's the totally wrong approach, but I can't see the forest for the trees. If you can help me, please make it "Javascript for Babies" picture book levels of simple, I cannot stress enough how inexperienced I am. This is just supposed to be a fun little extra thing for my D&D group.
function checkForTranslation(input, outputDiv) {
var input = document.getElementById("inputTextField").value;
var outputDiv = document.getElementById("translationOutputDiv");
input = input.toLowerCase();
//puts user input into an array and then outputs it word by word
const myArray = input.split(" "); //added .split, thank you James, still otherwise broken
let output = "";
let translation = "";
for (let i = 0; i < myArray.length; i++) {
output += myArray[i]; //up to here, this works perfectly to put each word in the string into an array
//prints all words but doesnt translate the second onwards
translation += myArray[i];
if (output == "") {
//document.getElementById("print2").innerHTML = "Translation Here";
}
else if (output == "apple") {
translation = "x-ray";
}
else if (output == "banana") {
translation = "yak";
}
else {
translation = "???";
}
output += " "; //adds a space when displaying original user input
} // END FOR LOOP
document.getElementById("print").innerHTML = output; //this outputs the original user input to the screen
document.getElementById("print3").innerHTML = translation; //this should output the translated output to the screen
} // END FUNCTION CHECKFORTRANSLATION
What it looks like
P.S. I'm not worried about Best Practices here, this is supposed to be a quickie project that I can send to a couple friends and they can open the HTML doc, saved locally, in their browser when they want to mess around with it if they want their half-orc character to say "die by my hammer!" or something. If you have suggestions for making it neater great, but I'm not worried about a mess, no one is going to be reading this but me, and hopefully once it's fixed I'll never have to read it again either!
Since it is a manual simple translation, you should just create a "dictionary" and use it to get the translations.
var dictionary = {
"apple": "x-ray",
"banana": "yak"
}
function checkForTranslation() {
var input = document.getElementById("inputTextField").value.toLowerCase();
var words = input
.split(' ') // split string to words
.filter(function(word) { // remove empty words
return word.length > 0
});
var translatedWords = words.map(function(word) {
var wordTranslation = dictionary[word]; // get from dictionary
if (wordTranslation) {
return wordTranslation;
} else { // if word was not found in dictionary
return "???";
}
});
var translatedText = translatedWords.join(' ');
document.getElementById("translationOutputDiv").innerHTML = translatedText;
}
document.getElementById('translate').addEventListener('click', function() {
checkForTranslation();
});
<input type="text" id="inputTextField" />
<button id="translate">translate</button>
<br/>
<hr />
<div id="translationOutputDiv"></div>
Or if you want it a little more organized, you could use
const dictionary = {
"apple": "x-ray",
"banana": "yak"
}
function getTranslation(string) {
return string
.toLowerCase()
.split(' ')
.filter(word => word)
.map(word => dictionary[word] || '???')
.join(' ');
}
function translate(inputEl, outputEl) {
outputEl.innerHTML = getTranslation(inputEl.value);
}
document.querySelector('#translate').addEventListener('click', function() {
const input = document.querySelector('#inputTextField');
const output = document.querySelector('#translationOutputDiv');
translate(input, output);
});
<input type="text" id="inputTextField" />
<button id="translate">translate</button>
<br/>
<hr />
<div id="translationOutputDiv"></div>

Add line break in string on last character before n characters

To start off, this is probably worded badly, as I am not sure how to put what I want into wordsLets say I have this canvas (I'm using node-canvas) and I want to make it display text from a user input. However, the way I am doing it limits the number of characters to 36-38 (not looking for a solution to this). So I made a script using the Regex textstr.match(/.{1,32}/g) that splits the string every 32 characters (just to be safe), calculates a new canvas height, and then does join("\n") when it comes time to print the string. However, when receiving feedback on this, I realized it would be better to split along the last space in the string and add a line break there, but I am confused how to do this.
My current code is this:
textStr = "123456789 01234567890 123456789012 34567890"
var splitStr
if(textstr.length > 32){
if(textstr.substring(1,32).includes(" ")){ //1,32 so it won't bug out if the first character is a space
//splitStr = textstr.something(test)
} else {
splitStr = textstr.match(/.{1,32}/g)
}
}
//canvas initialization blah blah blah
//load fonts yada yada yada
ctx.fillText(splitStr.join("\n"), 20, 55)
I was wondering if there was some sort of regex expression that I could use. Any help/feedback/common sense is appreciated
This solution is a bit complex and can due with some simplification. However it should get you mostly there.
const input = "123456789 01234567890 123456789012 34567890 11444444444 424124 1234124124121 4444444444444444444444444444444444444444444444444444444444444444444444";
const split = (value, width) => {
const stack = value.split(' ').reverse();
const results = [];
let builder = "";
while (stack.length > 0) {
const item = stack.pop();
if (item.length > width) { // is the current chunk already larger than our desired width?
if (builder !== "") { // we have to push our buffer too
results.push(builder);
builder = "";
}
results.push(item);
} else {
const line = builder === ""
? item
: `${builder} ${item}`;
if (line.length > width) { // is our new line greater than our width?
stack.push(item); // push the item back, since consuming it would make our line length too long. we let the next iteration consume it. results.push(builder); // push the buffer into our results.
builder = "";
} else if (stack.length === 0) { // is this the last element? just add it to the results.
results.push(line);
} else {
builder = line; // update our buffer to the current appended chunk.
}
}
}
return results;
};
split(input, 32).forEach((c) => console.log(c, c.length));
split(input, 32).join("\n");

javascript : How to filter text without spell checking?

The goal is to filter some "test" or "flood" messages in a shout box chat.
ex: when an user writes something like
aaaaaaaaaaaaaaaaaaaaa or jdhshjdskhdshuishifhduif or dsqjlkdsqjiodsqjiosqjdsjq
I want to filter such stupid words: I guess I need to write some functions like:
if string length>20 or string conatins more that 4 vowels in a row or contains 4 consonants in a row
or contains some special chars...
Maybe this function has aleardy been written to avoid reinventing the wheel.
regards
Well using some Regular Expressions could do the trick.
EDIT
I have updated the code after Chris's suggestion.
So the credit goes to him.
String.prototype.testVowels = function () {
return !(/([aeiou]){4,}\w*/g.test(this));
}
String.prototype.testConsonants = function () {
return !(/([bcdfghjklmnpqrstwxyz]){4,}\w*/g.test(this));
}
String.prototype.testLength = function() {
return this.length < 20;
}
function testString(str) {
var stringArr = str.split(" ");
// this will test for each word in the str parameter
stringArr.forEach(function(s) {
if(s.testConsonants() && s.testLength() && s.testVowels()) {
console.log("The word " + s + " is ok !");
}
});
}

Split a CSV string by line skipping newlines contained between quotes

If the following regex can split a csv string by line.
var lines = csv.split(/\r|\r?\n/g);
How could this be adapted to skip newline chars that are contained within a CSV value (Ie between quotes/double-quotes)?
Example:
2,"Evans & Sutherland","230-132-111AA",,"Visual","P
CB",,1,"Offsite",
If you don't see it, here's a version with the newlines visible:
2,"Evans & Sutherland","230-132-111AA",,"Visual","P\r\nCB",,1,"Offsite",\r\n
The part I'm trying to skip over is the newline contained in the middle of the "PCB" entry.
Update:
I probably should've mentioned this before but this is a part of a dedicated CSV parsing library called jquery-csv. To provide a better context I have added the current parser implementation below.
Here's the code for validating and parsing an entry (ie one line):
$.csvEntry2Array = function(csv, meta) {
var meta = (meta !== undefined ? meta : {});
var separator = 'separator' in meta ? meta.separator : $.csvDefaults.separator;
var delimiter = 'delimiter' in meta ? meta.delimiter : $.csvDefaults.delimiter;
// build the CSV validator regex
var reValid = /^\s*(?:D[^D\\]*(?:\\[\S\s][^D\\]*)*D|[^SD\s\\]*(?:\s+[^SD\s\\]+)*)\s*(?:S\s*(?:D[^D\\]*(?:\\[\S\s][^D\\]*)*D|[^SD\s\\]*(?:\s+[^SD\s\\]+)*)\s*)*$/;
reValid = RegExp(reValid.source.replace(/S/g, separator));
reValid = RegExp(reValid.source.replace(/D/g, delimiter));
// build the CSV line parser regex
var reValue = /(?!\s*$)\s*(?:D([^D\\]*(?:\\[\S\s][^D\\]*)*)D|([^SD\s\\]*(?:\s+[^SD\s\\]+)*))\s*(?:S|$)/g;
reValue = RegExp(reValue.source.replace(/S/g, separator), 'g');
reValue = RegExp(reValue.source.replace(/D/g, delimiter), 'g');
// Return NULL if input string is not well formed CSV string.
if (!reValid.test(csv)) {
return null;
}
// "Walk" the string using replace with callback.
var output = [];
csv.replace(reValue, function(m0, m1, m2) {
// Remove backslash from any delimiters in the value
if (m1 !== undefined) {
var reDelimiterUnescape = /\\D/g;
reDelimiterUnescape = RegExp(reDelimiterUnescape.source.replace(/D/, delimiter), 'g');
output.push(m1.replace(reDelimiterUnescape, delimiter));
} else if (m2 !== undefined) {
output.push(m2);
}
return '';
});
// Handle special case of empty last value.
var reEmptyLast = /S\s*$/;
reEmptyLast = RegExp(reEmptyLast.source.replace(/S/, separator));
if (reEmptyLast.test(csv)) {
output.push('');
}
return output;
};
Note: I haven't tested yet but I think I could probably incorporate the last match into the main split/callback.
This is the code that does the split-by-line part:
$.csv2Array = function(csv, meta) {
var meta = (meta !== undefined ? meta : {});
var separator = 'separator' in meta ? meta.separator : $.csvDefaults.separator;
var delimiter = 'delimiter' in meta ? meta.delimiter : $.csvDefaults.delimiter;
var skip = 'skip' in meta ? meta.skip : $.csvDefaults.skip;
// process by line
var lines = csv.split(/\r\n|\r|\n/g);
var output = [];
for(var i in lines) {
if(i < skip) {
continue;
}
// process each value
var line = $.csvEntry2Array(lines[i], {
delimiter: delimiter,
separator: separator
});
output.push(line);
}
return output;
};
For a breakdown on how that reges works take a look at this answer. Mine is a slightly adapted version. I consolidated the single and double quote matching to match just one text delimiter and made the delimiter/separators dynamic. It does a great job of validating entiries but the line-splitting solution I added on top is pretty frail and breaks on the edge case I described above.
I'm just looking for a solution that walks the string extracting valid entries (to pass on to the entry parser) or fails on bad data returning an error indicating the line the parsing failed on.
Update:
splitLines: function(csv, delimiter) {
var state = 0;
var value = "";
var line = "";
var lines = [];
function endOfRow() {
lines.push(value);
value = "";
state = 0;
};
csv.replace(/(\"|,|\n|\r|[^\",\r\n]+)/gm, function (m0){
switch (state) {
// the start of an entry
case 0:
if (m0 === "\"") {
state = 1;
} else if (m0 === "\n") {
endOfRow();
} else if (/^\r$/.test(m0)) {
// carriage returns are ignored
} else {
value += m0;
state = 3;
}
break;
// delimited input
case 1:
if (m0 === "\"") {
state = 2;
} else {
value += m0;
state = 1;
}
break;
// delimiter found in delimited input
case 2:
// is the delimiter escaped?
if (m0 === "\"" && value.substr(value.length - 1) === "\"") {
value += m0;
state = 1;
} else if (m0 === ",") {
value += m0;
state = 0;
} else if (m0 === "\n") {
endOfRow();
} else if (m0 === "\r") {
// Ignore
} else {
throw new Error("Illegal state");
}
break;
// un-delimited input
case 3:
if (m0 === ",") {
value += m0;
state = 0;
} else if (m0 === "\"") {
throw new Error("Unquoted delimiter found");
} else if (m0 === "\n") {
endOfRow();
} else if (m0 === "\r") {
// Ignore
} else {
throw new Error("Illegal data");
}
break;
default:
throw new Error("Unknown state");
}
return "";
});
if (state != 0) {
endOfRow();
}
return lines;
}
All it took is 4 states for a line splitter:
0: the start of an entry
1: the following is quoted
2: a second quote has been encountered
3: the following isn't quoted
It's almost a complete parser. For my use case, I just wanted a line splitter so I could provide a more granual approach to processing CSV data.
Note: Credit for this approach goes to another dev whom I won't name publicly without his permission. All I did was adapt it from a complete parser to a line-splitter.
Update:
Discovered a few broken edge cases in the previous lineSplitter implementation. The one provided should be fully RFC 4180 compliant.
As I have noted in a comment there is no complete solution just using single regex.
A novel method using several regexps by splitting on comma and joining back strings with embedded commas is described here:-
Personally I would use a simple finite state machine as described here
The state machine has more code, but the code is cleaner and its clear what each piece of code is doing. Longer term this will be much more reliable and maintainable.
It's not a good idea to use regex's to parse. Better to use it to detect the "bad" splits and then merge them back:
var lines = csv.split(/\r?\n/g);
var bad = [];
for(var i=lines.length-1; i> 0; i--) {
// find all the unescaped quotes on the line:
var m = lines[i].match(/[^\\]?\"/g);
// if there are an odd number of them, this line, and the line after it is bad:
if((m ? m.length : 0) % 2 == 1) { bad.push(i--); }
}
// starting at the bottom of the list, merge lines back, using \r\n
for(var b=0,len=bad.length; b < len; b++) {
lines.splice(bad[b]-1, 2, lines[bad[b]-1]+"\r\n"+lines[bad[b]]);
}
(This answer is licensed under both CC0 and WTFPL.)
Be careful- That newline is PART of that value. It's not PCB, it's P\nCB.
However, why can't you just use string.split(',')? If need be, you can run through the list and cast to ints or remove the padded quotation marks.

Categories

Resources