World!
I'm trying to create a program in Javascript that takes the log of a number typed into an HTML input. Unfortunately i've encountered a problem where it wont accept the string with the .replace().
Its Function:
I.E: When log(10) is calculated, the function should first remove the first 4 char's "log(" next remove the last parenthesis ")" and then take the log of the no. between.
HTML includes style elements, button and input form and an output < DIV >.
//Function
function calculate()
{
var inputString = document.getElementById("inpstr");
var output = document.getElementById("output");
//TESTING CODE
/*
if (inputString.value.startsWith("log(").endsWith(")"))
{
console.log(output.innerHTML = inputString.value.substring(4, 20).replace(")", ""));
}
else
{
output.innerHTML = "false";
}
*/
//Math.log() calc *****DOESNT WORK*****
if (inputString.value.startsWith("log(").endsWith(")"))
{
output.innerHTML = Math.log(inputString.value.replace(")", "").substring(4, 20));
}
else
{
output.innerHTML = inputString.value;
}
event.preventDefault();
}
If someone can give me an effective solution that would be much appreciated.
Thanks,
Syntax
Since Math.log() accepts only number values and you're trying to pass a string to it, you should first parse this value into a float number and then pass it to the log function:
let val = parseFloat(inputString.value.replace(")", "").substring(4, 20));
output.innerHTML = Math.log(val);
I'm guessing I got downvoted for being lazy, so here is the quick info. Gonras got it right relating to what you want to extract, but he forgot to check that what's being input is actually a log.
That's where the regex below comes in handy! I'm matching the field to:
^ start of word, since we want to match the entire field.
log(
([-.\d])) any consecutive sequence () of numbers (\d), -, and '.', represented by the []. The \(...\) makes sure to save this inner part for later.
$ is end of word, see 1.
res will be null if there is no match. Otherwise, res[0] is the entire match (so the entire input field) and res[1] is the first 'capture group', at point 3 - which is presumably the number.
This of course fails for multiple "-" inside, or "." etc... so think it over.
//Function
function calculate()
{
var inputString = document.getElementById("inpstr");
var output = document.getElementById("output");
var res = /^log\(([-.\d]*)\)$/.exec(inputString.value);
if (res)
output.innerHTML = Math.log(res[1]);
else
output.innerHTML = res;
}
document.getElementById("output").innerHTML='start';
calculate()
<div id='output'></div>
<input id='inpstr' value='log(2.71828)'></input>
If I wanted to fix your if to supplement Gonras's solution:
if (inputString.value.startsWith("log(") && inputString.value.endsWith(")"))
Yours fails since startsWith() returns a boolean, which obviously doesn't have a endsWith function.
Aanval op Vlemis (499|453) C44
This is what the string looks like. Though it's actually like this: "Aanval op variable (variable) variable
What I want to do is 1: get the coordinates (I already have this), 2 get Vlemis (first variable), get C44 (third variable) and check to see if the string is of this type.
My code:
$("#commands_table tr.nowrap").each(function(){
var text = $(this).find("input[id*='editInput']").val();
var attackername= text.match(/(?=op)[\s|\w]*(?=\()/);
var coordinates = text.match(/\(\d{1,3}\|\d{1,3}\)/);
});
Coordinates works, attackername however doesn't.
Html:
<span id="labelText[6]">Aanval op Vlemis (499|453) C44</span>
You should use one regex to take everything :
var parts = text.match(/(\w+)\s*\((\d+)\|(\d+)\)\s*(\w+)/).slice(1);
This builds
["Vlemis", "499", "453", "C44"]
If you're not sure the string is valid, test like this :
var parts = text.match(/(\w+)\s*\((\d+)\|(\d+)\)\s*(\w+)/);
if (parts) {
parts = parts.slice(1);
// do things with parts
} else {
// no match, yell at the user
}
I'm struggling with a ExtJS 4.1.1 grid that has editable cells (CellEditing plugin).
A person should be able to type a mathematic formula into the cell and it should generate the result into the field's value. For example: If a user types (320*10)/4 the return should be 800. Or similar if the user types (320m*10cm)/4 the function should strip the non-mathematical characters from the formula and then calculate it.
I was looking to replace (or match) with a RegExp, but I cannot seem to get it to work. It keeps returning NaN and when I do console.log(e.value); it returns only the originalValue and not the value that I need.
I don't have much code to attach:
onGridValidateEdit : function(editor,e,opts) {
var str = e.value.toString();
console.log(str);
var strCalc = str.match(/0-9+-*\/()/g);
console.log(strCalc);
var numCalc = Number(eval(strCalc));
console.log(numCalc);
return numCalc;
},
Which returns: str=321 strCalc=null numCalc=0 when I type 321*2.
Any help appreciated,
GR.
Update:
Based on input by Paul Schroeder, I created this:
onGridValidateEdit : function(editor,e,opts) {
var str = e.record.get(e.field).toString();
var strCalc = str.replace(/[^0-9+*-/()]/g, "");
var numCalc = Number(eval(strCalc));
console.log(typeof numCalc);
console.log(numCalc);
return numCalc;
},
Which calculates the number, but I am unable to print it back to the grid itself. It shows up as "NaN" even though in console it shows typeof=number and value=800.
Final code:
Here's the final code that worked:
onGridValidateEdit : function(editor,e,opts) {
var fldName = e.field;
var str = e.record.get(fldName).toString();
var strCalc = str.replace(/[^0-9+*-/()]/g, "");
var numCalc = Number(eval(strCalc));
e.record.set(fldName,numCalc);
},
Lets break this code down.
onGridValidateEdit : function(editor,e,opts) {
var str = e.value.toString();
What listener is this code being used in? This is very important for us to know, here's how I set up my listeners in the plugin:
listeners: {
edit: function(editor, e){
var record = e.record;
var str = record.get("your data_index of the value");
}
}
Setting it up this way works for me, So lets move on to:
var strCalc = str.match(/0-9+-*\/()/g);
console.log(strCalc);
at which point strCalc=null, this is also correct. str.match returns null because your regex does not match anything in the string. What I think you want to do instead is this:
var strCalc = str.replace(/[^0-9+*-]/g, "");
console.log(strCalc);
This changes it to replace all characters in the string that aren't your equation operators and numbers. After that I think it should work for whole numbers. I think that you may actually want decimal numbers too, but I can't think of the regex for that off the top of my head (the . needs to be escaped somehow), but it should be simple enough to find in a google search.
I have a chord chart application that I wrote and I would like to allow users to transpose the key of the chart using an onClick handler.
My chart looks like this
{C}My name is Blanket, {F}And I can run fast
The chords inside the brackets appear above the letter it preceeds.
I would like to use javascript or jquery in order to do this. How would I go about creating this transpose button? Any help is appreciated. Thank you in advance.
EDIT
So here's what I came up with...
$('.transposeUp').click(function(){
$('.chord').each(function(){
var currentChord = $(this).text(); // gathers the chord being used
if(currentChord == $(this).text()){
var chord = $(this).text().replace("F#", "G")
}
//... the if statements continue though every chord
//but I didn't place them here to save space
});
});
So here is the problem...
I have a slash chord in the mix (G/B), It changes the be on transpose but because it changes the "B" the chord is now (G/C) which is not the same as the "currentChord" so it doesn't change the G when it gets to its respective if condition. Until I have transposed enough where the Chord is eventualy (G/G) then the first "G" starts transposing leaving the last "G" the same. Any ideas? Again your knowledge and help is greatly appreciated. Thanks in advance.
You need to match the chords sequentially so that you can update them one at a time. If you try to match everything at once you'll run into problems like you described, because you keep matching the same chord over and over again.
A good way to accomplish this would be with regular expressions to parse and split the chord. Once you have the matching chord values, use an array of chords to find the next/previous chord to transpose. Here is some sample code I have developed as a demo:
<p><span class="chord">{C}</span>My name is Blanket,</p>
<p><span class="chord">{G / B}</span>And I can run fast</p>
<p>
<input id="transposeDown" type="button" value="Down" /> |
<input id="transposeUp" type="button" value="Up" />
</p>
var match;
var chords =
['C','C#','D','Eb','E','F','F#','G','Ab','A','Bb','B','C',
'Db','D','D#','E','F','Gb','G','G#','A','A#','C'];
var chordRegex = /C#|D#|F#|G#|A#|Db|Eb|Gb|Ab|Bb|C|D|E|F|G|A|B/g;
$('#transposeUp').click(function() {
$('.chord').each(function() {
var currentChord = $(this).text();
var output = "";
var parts = currentChord.split(chordRegex);
var index = 0;
while (match = chordRegex.exec(currentChord))
{
var chordIndex = chords.indexOf(match[0]);
output += parts[index++] + chords[chordIndex+1];
}
output += parts[index];
$(this).text(output);
});
});
$('#transposeDown').click(function() {
$('.chord').each(function() {
var currentChord = $(this).text();
var output = "";
var parts = currentChord.split(chordRegex);
var index = 0;
while (match = chordRegex.exec(currentChord))
{
var chordIndex = chords.indexOf(match[0],1);
output += parts[index++] + chords[chordIndex-1];
}
output += parts[index];
$(this).text(output);
});
});
Sample demo: http://jsfiddle.net/4kYQZ/2/
A couple of things to notice:
I took #gregp's idea of having multiple copies of the key list so that I can handle both sharps and flats. The first scale includes the preferred #/b which will be used during transposing. The second scale includes the other formats so that if the music includes them, they will transpose correctly. For instance, since Eb is in the first scale, it will be used instead of D# as you are transposing up and down; however, if there is a D# in the music to begin with, it will correctly move to D/E (with the caveat that if you move back, it will become an Eb again). Feel free to modify the preferred scale - I used my educated judgment based on personal music experience.
The regular expression has to have the sharped/flatted keys first, otherwise a C# would be just as easily matched as a C when that's not correct.
I have C at the beginning and the end of the array so that I can start at any location and move both directions without passing the end of the array. In order for this to work, the transposeDown code has an extra parameter in the call to chords.indexOf to start at position 1 so it matches the last C in the array instead of the first C. Then when it attempts to move to the previous element, it doesn't pass the beginning of the array.
I'm splitting the chord as well using the regular expression, which is redundant, but it makes it super-easy to recompose the final string back together - by interleaving the original string parts with the updated chord keys (don't forget the last piece at the end!).
I'm using elements instead of classes for your buttons.
Hope this helps!
Update 1: Per comment by OP, the use of indexOf on arrays is not supported by pre-ie9. This can be solved by using a helper function that does the same thing:
function arrayIndexOf(arr, match)
{
for (var i = 0; i < arr.length; i++)
if (arr[i] == match)
return i;
return -1;
}
And this line
var chordIndex = chords.indexOf(match[0]);
would be replaced with:
var chordIndex = arrayIndexOf(chords, match[0]);
See updated sample demo: http://jsfiddle.net/4kYQZ/11/
Using jQuery, you're spoiled for choice for binding click handlers. There's .click(), .delegate(), .live(), and many others. It wouldn't make sense to reproduce what the APIs already tell you, but I can say this: learning how to bind the click is the smallest part of the overall problem, and even capturing the chord name with .text() is going to be trivial once you've had a look at the jQuery API.
The tricky part is going to be the logic of transposing itself. You'll need to take the knowledge you already have (for example, going from E to F is only a half-step; there's no E# or Fb) and make it work as code.
My apologies for general advice rather than code samples, but my advice is to have two arrays, one containing all the chords in terms of sharps, one with all the chords in terms of flats.
You could do a bit of cheating, too: instead of logic to wrap around to the beginning (say, C in position 0), just have your arrays duplicated:
["C","C#","D","D#","E","F","F#","G","G#","A","B","C","C#","D","D#","E","F","F#","G","G#","A","B"]
Then find the first occurence of the chord name, move ahead the desired number of stops, and you'll still have the right string.
Call function transpose for up:
text = transpose(text, 1);
Call function transpose for down:
text = transpose(text, -1);
Function:
function transpose(text, amount){
var lines = new Array();
var chord = new Array();
var scale = ["C","Cb","C#","D","Db","D#","E","Eb","E#","F","Fb","F#","G","Gb","G#",
"A","Ab","A#","B","Bb","B#"];
var transp = ["Cb","C","C#","Bb","Cb","C","C","C#","D","Db","D","D#","C","Db","D",
"D","D#","E","Eb","E","F","D","Eb","E","E","E#","F#","E","F","F#",
"Eb","Fb","F","F","F#","G","Gb","G","G#","F","Gb","G","G","G#","A",
"Ab","A","A#", "G","Ab","A","A","A#","B","Bb","B","C","A","Bb","B",
"B","B#","C#"];
var inter = '';
var mat = '';
lines = text.split("\n");
for(var i in lines){
if(i%2===0){
chord = lines[i].split(" ");
for(var x in chord){
if(chord[x]!==""){
inter = chord[x];
var subst = inter.match(/[^b#][#b]?/g);
for(var ax in subst){
if(scale.indexOf(subst[ax])!==-1){
if(amount>0){
for(ix=0;ix<amount;ix++){
var pos = scale.indexOf(subst[ax]);
var transpos = 3*pos-2+3;
subst[ax] = transp[transpos+1];
}
}
if(amount<0){
for(ix=0;ix>amount;ix--){
var pos = scale.indexOf(subst[ax]);
var transpos = 3*pos-2+3;
subst[ax] = transp[transpos-1];
}
}
}
}
chord[x]=subst.join("");
}
}
lines[i] = chord.join(" ");
}
}
return lines.join("\n");
}
The first line is transpose and the second does not, and sequentially.
I want to take strings like:
Submit Changes
Create New
Update Record
Save Item
and convert them to:
Submitting Changes
Creating New
Updating Record
Saving Item
with a function like:
var ConvertToProgressivePresent = (function(){
// cache this regex
var rProgressivePresent = /\b(?:(Submi(t))|(Creat|Sav|Updat)e)\b/i;
return function(phrase){
return phrase.replace(rProgressivePresent, "$1$2$3ing");
};
}());
This above regex works but doesn't seem like the best way to do it. I don't like the grouping here where 1-2 groups are always empty when there is a match.
Got any suggestions or improvements?
If you have specific words to replace with then you could make a word bank. Have the words and their replacement stored in an object and then loop through it.
var ConvertToProgressivePresent = (function() {
var replaceValues = {
"Submit": "Submitting",
"Create": "Creating",
"Update": "Updating",
"Save": "Saving"
}
return function(phrase) {
for (var item in replaceValues) {
phrase = phrase.replace(item, replaceValues[item]);
}
return phrase;
};
}());
Here is a JSFiddle Example
I think you should probablly use CASE statments instead. Regex isn't the most efficient way of doing things...and that is probably best that it doesn't cuz you know the old saying.
Wow, I have this problem...I know, I'll use regex...ok, now you have two problems 90)
First off, it doesn't appear to me that your regex does quite what you want anyway in that I don't see a second "t" added when changing submit to submitting.
However, I don't think I would use regex for this task at all anyway. If you are just trying to replace one word with another, and the word always comes at the beginning of the string, I might do something like:
function ReplaceIfBeginsWith(wholeString, checkFor, replaceWith)
{
if (wholeString.indexOf(checkFor + ' ') == 0)
{
return replaceWith + ' ' + wholeString.substr(checkFor.length + 1);
}
if (wholeString == checkFor)
{
return replaceWith;
}
return wholeString;
}
Then, you can call the function with each of the words you would want to replace. If you want case-insensitivity, just check against lowercase versions of all the strings.