I was wondering about being able to use/make a function like REPT() to display partial repetitions with decimal numbers. My formula, as it stands, works fine for integers. If I wanted to give 3 stars, I just use =REPT(CHAR(9733), 3) and that prints 3 black stars.
Let's say I wanted to give something 4.2 stars. Is there a way to do something like this? I've been trying to figure out a way to do it with App Script, but I'm not sure how to proceed. Everything I've researched online is geared towards making a clickable rating system with HTML/CSS/JavaScript. But I'd be looking more for something like an average rating on Amazon or something.
This is what I have with App Script so far:
function starRating() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('test');
var cell = sheet.getRange(2,1); // Sets a test cell
// Create a concatenated string of 5 blank stars
var blankStars = "";
for (let i = 0; i < 5; i++) {
blankStars = blankStars.concat(String.fromCharCode(9734));
}
// Create a concatenated string of 5 black stars
var blackStars = "";
for (let i = 0; i < 5; i++) {
blackStars = blackStars.concat(String.fromCharCode(9733));
}
cell.setValue(blankStars);
cell.setHorizontalAlignment("center")
cell.setFontSize(18);
var rating = sheet.getRange(1,1).getValue(); // Raw Rating (e.g. 4.3)
var amount = Math.max(0, (Math.min(5, rating))) * 20; // Gets percent out of 100, also ensures rating is from 0-5
/*
Maybe find a way to overlay the blackStar on top of blankStar?
Use amount as a way of only showing percent of blackStar?
*/
}
As I put in the comment, my thought was to overlay a percent of the blackStar string on top of the blankStar string, but 1. I don't know how to do that and 2. I don't know how that could be put into the cell.
This is fairly close and a lot easier
function stars(n = 9) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const style = SpreadsheetApp.newTextStyle().setForegroundColor("#000000").setFontSize(16).build();
let s = "";
Array.from(new Array(10).keys()).forEach(x => s = s.concat(String.fromCharCode(x < n ? 9733 : 9734)));
sh.getRange(3, 1).setValue(s).setTextStyle(style);
}
I'm currently using the following JavaScript in a Google Chrome Extension to automate the 'add to cart' process for purchasing sneakers on nike.com;
var size_i_want = "11";
function fRun()
{enter code here
// Select size option.
var sizesList=document.getElementsByName("skuAndSize")[0];
for(var i=0; i<sizesList.length; i++)
{
if(sizesList.options[i].text.trim() == size_i_want)
{
sizesList.selectedIndex = i;
}
}
var aButtons = document.getElementsByTagName("button");
for(var i = 0; i < aButtons.length; ++i)
{
if(aButtons[i].className.indexOf("add-to-cart") > -1)
{
aButtons[i].click();
}
}
}
function fTick()
{
if(document.getElementsByName("skuAndSize")[0] != undefined)
{
setTimeout("fRun()", 600);
//fRun();
}else{
setTimeout("fTick()", 300);
}
}
setTimeout("fTick()", 300);
This script works perfectly for nike.com in the States, however does not work correctly for nike websites in other countries like the UK and Sweden.
As you can probably tell I am new to JavaScript and am still researching high and low to understand the language. However I understand this comes down to the fact that
var size_i_want = "11";
value is set as an integer (number) however on the Nike UK website the node that this affects contains letters, for example "UK 10.5".
Would somebody be able to help me declare a new variable and set it's value so that it contains both letters and numbers? I also have a feeling that this will impact the script as well, so help around that area is much appreciated too.
In javascript, variables are not typesafe, so you don't declare them as integers or strings. Coincidently:
var size_i_want = "11";
This is already a string. So you should already be able to add letters to it. Just change it to:
var size_i_want = "UK 10.5";
As millerbr already said javascript is not type safe.
The mix of letters and numbers should not have impact on the script, because
size_i_want = "11";
size_i_want = "UK 10.5";
are both strings.
I'm am trying to create a simple slider game using javascript. It's i simple 4 by 4 number slider game with each button being labeled 1-15 with the last block being a blank block. I just have no idea on how to scramble the buttons in a random order to start the game.
Below is the code I currently have.
<body>
<h1> Slider Game </h1>
<script type="text/javascript">
var blankrow = 3;
var blankcol = 3;
for (var r=0; r<4; r++)
{
for (var c=0; c<4; c++)
{
var bid = "b"+r+c;
var val = 4*r+c+1;
if (bid==="b33")
val = ' ';
var s = '<input type = "button" id = "' + bid + '" value = "'
+ val + '" onclick = "makeMove(this.id);" />' + '\n';
document.write (s);
}
}
</script>
<input type = "button" id = "btnScramble" value = "Scramble" onclick = "scrambleBoard();"/>
<input type = "button" id = "btnReset" value = "Reset Board" onclick = "resetBoard();"/>
</body>
I created a function like this:
function scrambleBoard()
{
}
I just have no idea where to go from here. I am just learning Javascript so I am still learning how to code. Thanks!
Update:
This is the make move function I have
function makeMove(btnid)
{
//is btnid next to blank
var r = btnid.substr(1,1);
var c = btnid.substr(2,2);
if (blankrow==r && blankcol==c+1) // check right
{
blankid="b"+r+c;
document.getElementById(blankid).value = document.getElementById(btnid).value;
document.getElementById(btnid).value = ' ';
blankcol=c;
}
else if (blankrow==r && blankcol==c-1) // check left
{
blankid="b"+r+c;
document.getElementById(blankid).value = document.getElementById(btnid).value;
document.getElementById(btnid).value = ' ';
blankcol=c;
}
else if (blankrow==r+1 && blankcol==c) // check bottem
{
blankid="b"+r+c;
document.getElementById(blankid).value = document.getElementById(btnid).value;
document.getElementById(btnid).value = ' ';
blankrow=r;
}
else if (blankrow==r-1 && blankcol==c) // check top
{
blankid="b"+r+c;
document.getElementById(blankid).value = document.getElementById(btnid).value;
document.getElementById(btnid).value = ' ';
blankrow=r;
} else
alert("Move is invalid");
}
Now with this how would I take the function (makeMove) and put it into the scramble function. Sorry I am really having a hard time understanding this concept.
You will need a makeMove function that fills the hole from a selected direction anyway, in order to make the game. Scramble is very simple: repeat the makeMove operation a sufficient number of times with a random neighbour (ignoring invalid neighbours like sliding from left at the left edge).
EDIT: Style-wise, document.write is considered to be a bad practice. Much better would be to make an element such as
<div id="board"></div>
and then fill it up by either creating documents with document.createElement and adding it there, which is a bit of a pain, or you can go the easy route and assign HTML markup to innerHTML:
document.getElementById('board').innerHTML = allMyButtonsHTML;
Also, using onclick="..." is considered a bad practice; try to get used to not mixing JavaScript and HTML by simply leaving off the onclick="...", and instead assigning it from JavaScript:
var scrambleButton = document.getElementById('btnScramble');
scrambleButton.addEventListener('click', function() {
...
});
None of this is an error as it stands, but it will result in cleaner, more maintainable code in the future.
EDIT2: You would not be putting makeMove into the shuffle, you'd be calling it from there.
function shuffleBoard() {
// the hole starts here
var holeRow = 3, holeCol = 3;
// the number of shuffling moves
var moves = 100;
// repeat while moves is not yet at zero
loop: while (moves) {
// we want to move one space from our current hole, so start there
var nextRow = holeRow, nextCol = holeCol;
// get a random number from 0 to 3 using the |0 hack
// to convert a real number to an integer
var direction = (Math.random() * 4)|0;
// now to see what coordinate changes...
switch (direction) {
case 0:
// if we're going right, we increment the column
// if that puts us too far right, we jump to the start of the loop
// to pick a new direction again
if (nextCol++ > 3) continue loop;
break;
case 1:
// same deal for down
if (nextRow++ > 3) continue loop;
break;
case 2:
// and left
if (nextCol-- < 0) continue loop;
break;
case 3:
// and up
if (nextRow-- > 0) continue loop;
break;
}
// this should be more elegant but
// like this it will fit in with your existing function
makeMove('b' + nextRow + nextCol);
// now since we moved the hole, we update its current position
holeRow = nextRow;
holeCol = nextCol;
// that's one move down, lots to go!
moves--;
}
// or is it? nope, all done.
}
After you add the first button, loop through however many more you need to create. Use math.random to pick a random number 0-1. If the number is 0, use insertadjacenthtml to add a new button to the left. If the number is 1, add the button to the right.
I am currently building a filter based on div class's and contents.
I was wondering if it is possible to pass a string like follows into a function:
"£0.01 - £100.01"
and then have the function show all div's where the html of that div is between this range
so say I have a div with a class of "price" and its contents were: £10.30
from running this function and passing the string of "£0.01 - £100.01" into it it would hide all div's similar to how I have done it in the js below then only show the div's where the div class "price"'s contents were within the selected price range.
I have managed to do something similar with a brand filter which I will provide here:
function brand(string){
var brand = string;
$('.section-link').hide();
$('.section-link').children('.brand.' + brand).parent().show();
if (brand == "All Brands"){
$('.section-link').show();
}
}
Any general advice or code is greatly appreciated to help achieve this :)
Thanks,
Simon
Edit:
Target div example:
<div class="section-link">
<div class="price"> £56.99</div>
</div>
Reply's are helping a lot, the filter function looks awesome so thanks for pointing that out.
I am just trying to find a way to split the initial string being past in, into two values one low and one high as well as stripping the £ signs
Edit:
managed to split the original string:
var range = string.replace(/\u00A3/g, '');
var rangearray = range.split("-");
alert(rangearray[0]);
alert(rangearray[1]);
FINAL EDIT:
From the reply's I have kind of been able to make a function, however it is not entirely working :) can anyone spot what I have done wrong?
function price(string){
$('.section-link').hide();
var range = string.replace(/\u00A3/g, '');
var rangearray = range.split("-");
low = rangearray[0];
high = rangearray[1];
$('.section-link').children('.price').each(function() {
var divprice = $(this).text().replace(/\u00A3/g, '');
if (low <= divprice && high >= divprice){
$(this).parent().show();
}
})
}
Okay its working, I had spaces in my string. The final function (although messy :P) is:
function price(string){
$('.section-link').hide();
var range = string.replace(/\u00A3/g, '');
var rangearray = range.split("-");
low = rangearray[0].toString();
high = rangearray[1].toString();
lowmain = low.replace(/ /g,'');
highmain = high.replace(/ /g,'');
$('.section-link').children('.price').each(
function() {
var divprice = $(this).text().replace(/\u00A3/g, '');
var maindivprice = divprice.replace(/ /g,'');
if (lowmain <= maindivprice && highmain >= divprice){
$(this).parent().show();
}
})
}
I'd use a function like this one, where range is the string you gave
function highlightDivs(range) {
var lower = range.split(" ")[0].slice(1);
var upper = range.split(" ")[2].slice(1);
$('.section-link').hide();
$('.section-link').children('.price').each(function() {
if (lower <= $(this).val() && upper >= $(this).val()){
$(this).parent().show();
}
});
}
You can use jQuery's build in filter() function, and write a filter with the condition you described.
First, you should hide all the items with any price.
$(".price").parent().hide();
Then, you can filter all the items with in-range prices and show them:
$(".price").filter(function(){
var $this = $(this);
var value = $this.val();
return (value >= minNumber && value <= maxNumber); // returns boolean - true will keep this item in the filtered collection
}).parent().show();
Use jQuery's filter()
An example -> http://jsfiddle.net/H6mtY/1/
var minValue = 0.01,
maxValue = 100.01;
var filterFn = function(i){
var $this = $(this);
if($this.hasClass('amount')){
// assume that text is always a symbol with a number
var value = +$this.text().match(/\d+.?\d*/)[0];
if(value > minValue && value < maxValue){
return true;
}
}
return false;
};
// apply your filter to body for example
$('#target span')
.filter(filterFn)
.each(function(i,ele){
// do something with the selected ones
$(this).css('color','red');
});
I would go by something like:
Get all the divs that have prices.
Iterate through all:
Transform the strings (minus the pound symbol) to float numbers and compare with an IF statement if they are inside the provided range.
If they are just go to the next (use continue maybe)
Else (not in the range) add a class like .hide so it can be blended through css (or just use the blend function from jquery)
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.