I want to print out the content of the composed array. As you can see I call console.log at the end of the code and every value in the composed array is undefined. But if I call console.log at somewhere else such as if statement, the values are right. Please tell me why, thanks!
$(function() {
var text = $('p').text().split('');
var letter = 0;
var composed = [];
$.each(text, function(index, value) {
if (/\w/.test(value)) { // if the character is alphanumeric
var prev = index - 1;
var next = index + 1;
if (/\w/.test(text[prev])) {
composed[letter] = composed[letter] + value;
if (!/\w/.test(text[next])) {
console.log('composed[' + letter + '] = ' + composed[letter]);
letter++;
}
} else {
composed[letter] = value;
if (!/\w/.test(text[next])) {
console.log('composed[' + letter + '] = ' + composed[letter]);
letter++;
}
}
} else {
composed[letter] = value;
console.log('composed[' + letter + '] = ' + composed[letter]);
letter++;
}
console.log('composed[' + letter + '] = ' + composed[letter]); // undefined
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Test both Chinese中文 ,alphbets and numbers.</p>
console.log('composed[' + letter + '] = ' + composed[letter - 1]);
this gives correct value
your composed[letter] is "undefined" because previous state is letter++;
Perhaps you meant this?
var letter = 0;
var composed = [];
function addLetter(letter, value) {
// your could ignore white-space and punctuation there
if (composed[letter]) composed[letter] += value
else composed[letter] = value;
}
$(function() {
var text = $('p').text().split('');
$.each(text, function(index, value) {
if (/\w/.test(value)) { // if the character is alphanumeric
var prev = index - 1;
var next = index + 1;
if (/\w/.test(text[prev])) {
addLetter(letter, value)
if (!/\w/.test(text[next])) {
letter++;
}
} else {
addLetter(letter,value);
if (!/\w/.test(text[next])) {
letter++;
}
}
} else {
addLetter(letter, value)
letter++;
}
});
console.log(composed)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Test both Chinese中文 ,alphabets and numbers 1234.</p>
Just add a check for spaces. to ensure the we are not manipulating spaces.
**if (/\w/.test(text[prev] && composed[letter]!=" "))**
--
$(function() {
var text = $('p').text().split('');
var letter = 0;
var composed = [];
$.each(text, function(index, value) {
if (/\w/.test(value)) { // if the character is alphanumeric
var prev = index - 1;
var next = index + 1;
if (/\w/.test(text[prev] && composed[letter]!=" ")) {
composed[letter] = composed[letter] + value;
if (!/\w/.test(text[next])) {
console.log('composed[' + letter + '] = ' + composed[letter]);
letter++;
}
} else {
composed[letter] = value;
if (!/\w/.test(text[next]) && composed[letter]!=" ") {
console.log('composed[' + letter + '] = ' + composed[letter]);
letter++;
}
}
} else {
composed[letter] = value;
console.log('composed[' + letter + '] = ' + composed[letter]);
letter++;
}
console.log('composed[' + letter + '] = ' + composed[letter]); // undefined
});
});
Related
I'm creating a basic .fountain editor to html parser that pretty much checks for words typed in a textarea on keyup.
Line by line parsing works below, but of course users would type in " **bold** then *italic* " in a single line but I can't seem to get it to scan each word in a line.
Below is my code:
jQuery.fn.parseAsFountain = function( window, jQuery ){
jQuery = $;
var storybox = $(this).val();
story = storybox.split('\n');
// Process The entire box
for( var i=0;i<story.length;i++ ){
var line = story[i];
SubstitutePerLine(line);
}
story_cleared = story.join('\n');
story_breaks = story_cleared.replace(/(?:\r\n|\r|\n)/g, '<br />');
return story_breaks;
function SubstitutePerLine(line){
if( line.match(/^[*].*[*]$/) ){
newval = line.slice(1, -1);
if( newval.match(/^[*].*[*]$/) ){
newval2 = newval.slice(1, -1);
if( newval2.match(/^[*].*[*]$/) ){
newval3 = newval2.slice(1, -1);
if( newval3.match(/\\/) ) {
if(newval3.match(/\\\*/)){
slash_val = newval3.replace(/\\/, '');
story[i] = '<b><i>' + slash_val + '</i></b>';
}
else {
story[i] = '<b><i>' + newval3 + '</i></b>';
}
}
else {
story[i] = '<b><i>' + newval3 + '</i></b>';
}
}
else if( newval2.match(/\\/) ) {
if(newval2.match(/\\\*/)){
slash_val = newval2.replace(/\\/, '');
story[i] = '<b>' + slash_val + '</b>';
}
else {
story[i] = '<b>' + newval2 + '</b>';
}
}
else {
story[i] = '<b>' + newval2 + '</b>';
}
}
else if( newval.match(/\\/) ) {
if(newval.match(/\\\*/)){
slash_val = newval.replace(/\\/, '');
story[i] = '<i>' + slash_val + '</i>';
}
else {
story[i] = '<i>' + newval + '</i>';
}
}
else {
story[i] = '<i>' + newval + '</i>';
}
}
if( line.match(/#/) ){
newval = line.replace(/^#/, '');
story[i] = '<p hidden>' + newval + '</p>';
}
if( line.match(/##/) ){
newval = line.replace(/^##/, '');
story[i] = '<p hidden>' + newval + '</p>';
}
if( line.match(/###/) ){
newval = line.replace(/^###/, '');
story[i] = '<p hidden>' + newval + '</p>';
}
return story;
}
}
I've tried re-splitting it by word in another split(" ") inside the loop then joining it again afterwards but to no avail.
Parsing markup is not a straightforward task. To name a few things:
If an asterisk is followed by a space, it will not be the start of a formatted piece of text;
Several parts can be formatted in one line, not only single words, but also word groups;
Formatting may even cross over a line break.
There might be an even number of slashes preceding an asterisk, in which case the asterisk is not escaped.
Here is a solution that only deals with italic, bold and italic+bold formatting, and the removal of escaping slashes. I did not deal with the hashes (#) as this was already quite broad for a Q&A:
jQuery.fn.parseAsFountain = function() {
var txt = this.val(),
re = /(\s?)(\\*)(\*+)(?=(\s?))/g,
arr,
openAsterisks = 0,
result = [],
last = 0,
start;
while ((arr = re.exec(txt)) !== null) {
var [all, prefix, escaped, asterisks, suffix] = arr;
if (escaped.length % 2) { // First asterisk is escaped
escaped += asterisks[0];
asterisks = asterisks.substr(1);
if (!asterisks.length) continue; // Nothing to do
}
var useAsterisks = 0;
if (openAsterisks && !prefix.length) {
useAsterisks = Math.min(openAsterisks, asterisks.length);
// Add HTML for bold, italic or both
result.push(
txt.substr(last, start - useAsterisks - last),
['<i>','<b>','<i><b>'][useAsterisks-1],
txt.substr(start, arr.index + escaped.length - start),
['</i>','</b>','</b></i>'][useAsterisks-1]);
last = arr.index + escaped.length + useAsterisks;
openAsterisks = 0;
}
if (!openAsterisks && asterisks.length > useAsterisks && !suffix.length) {
openAsterisks = asterisks.length - useAsterisks;
start = arr.index + prefix.length + escaped.length + asterisks.length;
}
}
// Flush remaining text
result.push(txt.substr(last));
// Remove escaping slashes (not escaped ones!):
result = result.join('').replace(/\\(.)/g, '$1');
return result;
}
$('textarea').on('input', function () {
$('pre').html($(this).parseAsFountain());
}).trigger('input');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea style="width:100%" rows=5>
This is **a** test *with several* ***formats
but*** keeping asterisks \*where* needed.
</textarea>
<h3>Rendered:</h3>
<pre>
</pre>
var swTitle = {};
var favorite = [];
$.each($("input[name='Title']:checked"), function() {
favorite.push($(this).val());
console.log($("input[name='Title']:checked"));
});
swTitle.domain = favorite;
var List = {};
for (var m = 0; m < favorite.length; m++) {
var swTitleObj = [];
$.each($('input[name="' + swTitle.domain[m] + '"]:checked'), function() {
console.log(swTitle.domain[m]);
swTitleObj.push($(this).attr("class"));
console.log(swTitleObj);
});
List[swTitle.domain[m]] = swTitleObj;
}
var swSkillData = " ";
$.each(List, function(key, value) {
console.log(key + ":" + value);
swSkillData += '<li>' + key + ' ' + ':' + ' ' + value + '</li>';
});
Output will be like:
Fruits:Apple,Banana,Orange,Grapes
I want my output be like:
Fruits:Apple,Banana,Orange & Grapes
I have an array of keys and values separated by commas. I want to insert "and" and remove the comma before the last checked element. Kindly help me out with this issue.
I think you can reduce your code, with an option of adding and before the last element like,
var inputs=$("input[name='Title']:checked"),
len=inputs.length,
swSkillData='',
counter=0;// to get the last one
$.each(inputs, function() {
sep=' , '; // add comma as separator
if(counter++==len-1){ // if last element then add and
sep =' and ';
}
swSkillData += '<li>' + this.value + // get value
' ' + ':' + ' ' +
this.className + // get classname
sep + // adding separator here
'</li>';
});
Updated, with and example of changing , to &
$.each(List, function(key, value) {
console.log(key + ":" + value);
var pos = value.lastIndexOf(',');// get last comma index
value = value.substring(0,pos)+' & '+value.substring(pos+1);
swSkillData += '<li>' + key + ' ' + ':' + ' ' + value + '</li>';
});
Snippet
var value ='Apple,Banana,Orange,Grapes';var pos = value.lastIndexOf(',');// get last comma index
value = value.substring(0,pos)+' & '+value.substring(pos+1);
console.log(value);
Here is an easy and customizable form of doing it.
(SOLUTION IS GENERIC)
$(document).ready(function() {
var ara = ['Apple','Banana','Orange','Grapes'];
displayAra(ara);
function displayAra(x) {
var str = '';
for (var i = 0; i < x.length; i++) {
if (i + 1 == x.length) {
str = str.split('');
str.pop();
str = str.join('');
str += ' and ' + x[i];
console.log(str);
$('.displayAra').text(str);
break;
}
str += x[i] + ',';
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Fruits : <span class="displayAra"></span>
str = str.replace(/,(?=[^,]*$)/, 'and')
I solved my own issue. I replaced my last comma with "and" using the above regex. Thanks to Regex!!!
I have function that display ASCII table in monospace font
function ascii_table(array, header) {
if (!array.length) {
return '';
}
for (var i = array.length - 1; i >= 0; i--) {
var row = array[i];
var stacks = [];
for (var j = 0; j < row.length; j++) {
var new_lines = row[j].toString().split("\n");
row[j] = new_lines.shift();
stacks.push(new_lines);
}
var new_rows_count = Math.max.apply(Math, stacks.map(function(column) {
return column.length;
}));
for (var k = new_rows_count - 1; k >= 0; k--) {
array.splice(i + 1, 0, stacks.map(function(column) {
return column[k] || "";
}));
}
}
var lengths = array[0].map(function(_, i) {
var col = array.map(function(row) {
if (row[i] != undefined) {
return row[i].length;
} else {
return 0;
}
});
return Math.max.apply(Math, col);
});
// column padding
array = array.map(function(row) {
return '| ' + row.map(function(item, i) {
var size = item.length;
if (size < lengths[i]) {
if (item.match(/\t/g)) {
// tab have 4 spaces
size += item.match(/\t/g).length*3;
}
item += new Array(lengths[i] - size + 1).join(' ');
}
return item;
}).join(' | ') + ' |';
});
var sep = '+' + lengths.map(function(length) {
return new Array(length + 3).join('-');
}).join('+') + '+';
if (header) {
return sep + '\n' + array[0] + '\n' + sep + '\n' +
array.slice(1).join('\n') + '\n' + sep;
} else {
return sep + '\n' + array.join('\n') + '\n' + sep;
}
}
but fail if table cell contain japanese letters like 中文 when one character have width of two ascii letters:
中文
rock
How can I deal with cases like this? How can I know if width of a character have size of two?
You'll need to check the characters width rather than length. Anything that is a (decent) port of wcwidth should do the trick.
I'm trying to make a palindrome script and it's actually "working".
I want to make a improvement. I want the input value append on my "output" div.
here are my fiddle http://jsfiddle.net/vitorboccio/8yh1u0t7/
any hints ? I dont want to use Jquery ! Thanks!
(function () {
"use strict";
var output = document.getElementById("output"),
statusLine = document.getElementById("status"),
phrase = document.getElementById('phrase'),
testButton = document.getElementById("testButton"),
//palindromeText = document.getElementById("palindrome"),
characterCheck = document.getElementById("characterCheck"),
ignoreSpecialCharacters = false,
ignoreSpaces = false;
function setMessage(palindrome) {
if (palindrome) {
output.innerHTML = phrase.value + ' ' + "é palindroma";
} else {
output.innerHTML = phrase.value + ' ' + "não é a palindroma";
}
}
function checkForPalindrome(string) {
var palindrome = true,
right = string.length - 1,
left = 0;
if (!string || string.length < 1) {
// 0 characters
return false;
}
while (left < right && palindrome) {
palindrome = string.charAt(left) === string.charAt(right);
left++;
right--;
}
return palindrome;
}
function executeTest() {
var string = phrase.value,
cleanString;
cleanString = string;
if (ignoreSpaces) {
//ignores whitespaces only;
cleanString = string.replace(/\s+/g, '');
}
if (ignoreSpecialCharacters) {
//ignores punctuation and white space (controversial).
cleanString = string.replace(/[A-Z0-9]/ig, '');
}
if (checkForPalindrome(cleanString)) {
setMessage(true);
palindromeText.innerHTML = '"' + string + '"';
} else {
setMessage(false);
}
}
function executeOnEnter(e) {
if (e.keyCode === 13) {
executeTest();
// phrase.blur();
}
}
//resets the form to state 1
function resetForm() {
output.innerHTML = "";
//statusLine.innerHTML = "Waiting";
statusLine.style.color = "green";
phrase.value = "";
}
function charIgnoreChanged(e) {
ignoreSpecialCharacters = e.target.checked;
}
function spaceIgnoreChanged(e) {
ignoreSpaces = e.target.checked;
}
phrase.addEventListener('keydown', executeOnEnter);
testButton.addEventListener('click', executeTest);
characterCheck.addEventListener('change', charIgnoreChanged);
spaceCheck.addEventListener('change', spaceIgnoreChanged);
}());
You just need to modify setMessage
function setMessage(palindrome) {
if (palindrome) {
output.innerHTML += phrase.value + ' ' + "é palindroma<br />";
} else {
output.innerHTML += phrase.value + ' ' + "não é a palindroma<br />";
}
// for user convenience, clear the textbox and give it focus
phrase.value = '';
phrase.focus();
}
Fiddle
You can append to the div by keeping the original innerHTML like this:
output.innerHTML = output.innerHTML + "<br />" + phrase.value + ' ' + "é palindroma";
or shorter:
output.innerHTML += "<br />" + phrase.value + ' ' + "é palindroma";
i am making a number sorter that alerts the number of different numbers (e.g. 11223 would be returned as number of ones = 2, number of twos = 2, number of threes = 1 ect. ect.)
here is the code
<html>
<body>
<textarea width="100" height="50" id="text" onBlur="sort();"></textarea>
<script>
var text = document.getElementById("text").value;
var no1 = 0
var no2 = 0
var no3 = 0
var no4 = 0
var no5 = 0
var no6 = 0
var no7 = 0
var no8 = 0
var no9 = 0
var no0 = 0
var other = 0
var split = text.split("");
function sort() {
for (i = 1; i < split; i++) {
if (string[i]===1) {
no1++;
return no1;
}
else if (string[i]===2) {
no2++;
return no2;
}
else if (string[i]===3) {
no3++;
return no3;
}
else if (string[i]===4) {
no4++;
return no4;
}
else if (string[i]===5) {
no5++;
return no5;
}
else if (string[i]===6) {
no6++;
return no6;
}
else if (string[i]===7) {
no7++;
return no7;
}
else if (string[i]===8) {
no8++;
return no8;
}
else if (string[i]===9) {
no9++;
return no9;
}
else if (string[i]===0) {
no0++;
return no0;
}
else {
other++;
return other;
}
}
alert("number of ones = " + no1 + ", number of twos = " + no2 + ", number of threes = " + no3 + ", number of fours = " + no4 + ", number of fives = " + no5 + ", number of sixes = " + no6 + ", number of sevens = " + no7 + ", number of eights = " + no8 + ", number of nines = " + no9 + ", number of zeros = " + no0 + ", number of other characters = " + other + ".");
}
</script>
</body>
</html>
when i enter a value and click away from the text field it returns 0 for all the variables
please help
Your code was not very easy to maintain, so I made my own version, try it out:
function sort() {
var text = document.getElementById("text").value;
// will contain a list of chars with their associated count
var charCounter = {};
for (var i = 0, l=text.length; i < l; i++) {
var char = text[i];
// if char is a number
if(!isNaN(parseInt(char))){
if(charCounter[char]){ charCounter[char]++; }
else { charCounter[char] = 1; }
// if char is not a number
} else {
if(charCounter['other characters']){ charCounter['other characters']++; }
else { charCounter['other characters'] = 1; }
}
}
outputList(charCounter);
}
function outputList(obj){
var keys = Object.keys(obj);
var output = '';
for(var i=0, l=keys.length; i<l; i++){
output += 'Number of ' + keys[i] + ' : ' + obj[keys[i]] + '.\n';
}
alert(output);
}
JS Fiddle Demo
A simpler solution is to use an array of words for the number names and use it for keys on an object that accumulates the count of occurrences of each digit, e.g.
function countDigits(n) {
// Array of number names
var words = ['zeros','ones','twos','threes','fours',
'fives','sixes','sevens','eights','nines'];
// Split number into digits
var nums = String(n).split('');
var counts = {};
// Count how many of each digit
nums.forEach(function(n){
if (!(counts.hasOwnProperty(words[n]))) counts[words[n]] = 0;
++counts[words[n]];
});
// Write to output
words.forEach(function(w){
console.log('Number of ' + w + ' = ' + (counts[w] || 0) + '\n');
});
}
countDigits(1012023405);
/*
Number of zeros = 3
Number of ones = 2
Number of twos = 2
Number of threes = 1
Number of fours = 1
Number of fives = 1
Number of sixes = 0
Number of sevens = 0
Number of eights = 0
Number of nines = 0
*/
Or a slightly different formulation:
function countDigits(n) {
// Array of number names
var words = ['zeros','ones','twos','threes','fours',
'fives','sixes','sevens','eights','nines'];
var counts = [];
// Split number into digits and count
String(n).split('').forEach(function(n) {
counts[n] = counts[n]? counts[n] + 1 : 1;
});
// Write to output
for (var i=0; i<10; i++) {
console.log('Number of ' + words[i] + ' = ' + (counts[i] || 0) + '\n');
}
}