I'm trying to setValue for each cell separately while loop is running.
for (var i = 5; i <= lastRow; i++) { // Start from row 5
var pos = sheet.getRange([i], 1); // 1 == column A, A=1,B=2,C=3 etc.
var posValue = (pos.isPartOfMerge() ? pos.getMergedRanges()[0].getCell(1, 1) : pos).getValue(); // get value of marged cell
var animalColor = sheet.getRange([i], 9); // 9 == column I
if (posValue == 'Cat') {
animalColor.setValue('brown');
} else if (posValue == 'Dog') {
animalColor.setValue('black');
} else if {
animalColor.setValue('none');
}
}
I want to go from this:
To this:
I tried to write additional IF statements but that just felt dumb and code will get heavier.
Create an array of colors for each animal. Then you can get the next value from the appropriate animal's array.
const colors = {
Dog: {
colors: ['white', 'black', 'red', 'grey'],
index: 0
},
Cat: {
colors: ['brown', 'black', 'white', 'gray'],
index: 0
},
Bird: {
colors: ['yellow', 'red', 'blue', 'purple'],
index: 0
}
};
for (var i = 5; i <= lastRow; i++) { // Start from row 5
var pos = sheet.getRange([i], 1); // 1 == column A, A=1,B=2,C=3 etc.
var posValue = (pos.isPartOfMerge() ? pos.getMergedRanges()[0].getCell(1, 1) : pos).getValue(); // get value of marged cell
var animalColor = sheet.getRange([i], 9); // 9 == column I
if (posValue in colors) {
animalColor.setValue(colors[posValue].colors[colors[posValue].index++]);
}
}
Alternate solution
You can use the includes,map and setValues methods in getting the desired output. You may use the script below as a guide.
Script:
const colors = {
"Cat": ["brown", "black", "white", "gray"],
"Dog": ["white", "black", "red", "gray"],
"Bird": ["yellow", "red", "blue", "purple"]
};
function transformTable() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); // Get the active sheet
var range = sheet.getRange("A1:A"); // Get the range of the data
var values = range.getValues().flat().filter(x => x); // Get the values of the data
var animalsInData = Object.keys(colors);
var output = [];
values.map(x => (animalsInData.includes(x))? colors[x].map(y=>output.push([y])) : output.push([""]));
sheet.getRange(1,3,output.length,1).setValues(output); // edit the range accordingly
}
Output:
Please note that I have added snake as a test case if the script will run even if there is an additional animal outside the colors object
References:
setValues method
JavaScript Array map
JavaScript String includes
Related
I have a function that change the background color of cells.
Function works well and do the thing I want, but I met one problem that I don't really know how to solve.
I want this function to loop through all used columns in spreadsheet. (for now it is from G till TP column will increase)
As you can see the function I have now do the thing only with G column.
How to make it loop till the last used column?
function insertColor2() {
const sheetName = "結果1"; // set the sheet name.
// 1. Retrieve values from sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const values1 = sheet.getRange(3, 7, sheet.getLastRow() - 2,1).getValues();
const values = [];
values.push(values1);
// 2. Create an array for modifying the background colors.
const backgroundColors = values.map(([,,c,,,...g]) =>
g.reduce((o, e) => {
if (e.toString() != "") {
o.total += e;
o.colors.push(c >= o.total ? null : "red");
} else {
o.colors.push(null);
}
return o;
}, {colors: [], total: 0}).colors
);
const flatten = [].concat.apply([], backgroundColors);
const newArr = [];
while(flatten.length) newArr.push(flatten.splice(0,1));
Logger.log(newArr);
// 3. Modify the background colors of cells.
sheet.getRange(8, 7, newArr.length, 1).setBackgrounds(newArr);
}
So I found a solution to solve this problem. It is not the best solution but works well. I just simply transposed array twice at the beginning and before inputting result to the sheet.
function transpose(a) {
return Object.keys(a[0]).map(function(c) {
return a.map(function(r) { return r[c]; });
});
}
function transpose1(original) {
var copy = [];
for (var i = 0; i < original.length; ++i) {
for (var j = 0; j < original[i].length; ++j) {
// skip undefined values to preserve sparse array
if (original[i][j] === undefined) continue;
// create row if it doesn't exist yet
if (copy[j] === undefined) copy[j] = [];
// swap the x and y coords for the copy
copy[j][i] = original[i][j];
}
}
return copy;
}
function insertColor5() {
const sheetName = "結果1"; // Please set the sheet name.
// 1. Retrieve values from sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const values1 = sheet.getRange(3, 7, sheet.getLastRow() - 2, sheet.getLastColumn()).getValues();
const values = transpose(values1);
//Logger.log(values);
// 2. Create an array for modifying the background colors.
const backgroundColors = values.map(([,,c,,,...g]) =>
g.reduce((o, e) => {
if (e.toString() != "") {
o.total += e;
o.colors.push(c >= o.total ? null : "red");
} else {
o.colors.push(null);
}
return o;
}, {colors: [], total: 0}).colors
);
const kolorki = transpose1(backgroundColors);
//Logger.log(transpose1(backgroundColors));
// 3. Modify the background colors of cells.
sheet.getRange(8, 7, kolorki.length, kolorki[0].length).setBackgrounds(kolorki);
}
I am trying to create an array of nine random words. Each word should have a color key value. Out of the nine words 3 words should have the color 'red', 3 words should have the color 'blue', and one word shoule have the color 'black'. The color needs to be assigned to the word randomly (each time the array is produced the positioning of the each color should change randomly).
My code so far produces and array of words each randomly assigned a color however with no limit - for example I may get 2 black 3 red and 4 blue. Then the next time 0 black 4 red and 5 blue. I need to produce 4 red 4 blue and 1 black every time (randomly positioned).
I thought that a counter would be useful however currently is not being used.
Any help would be great - I am new to coding so be critical!
Here is my code:
//Globals
const wordList = [..... ***remov
ed giant list of words from post***
];
const colorList = ['red', 'blue', 'black']
let randomWordNum = Math.floor(Math.random()*(wordList.length))
let randomColorNum = Math.floor(Math.random()*(colorList.length))
let coloredWords = [];
let redCounter = 0;
let blueCounter = 0;
let blackCounter = 0;
//Square function
//assigns color and word value to object key
//pushes object to new array 'words'
const createSquare = () => {
let randomWordNum = Math.floor(Math.random()*(wordList.length))
let randomColor = colorList[Math.floor(Math.random()*(colorList.length))]
if (randomColor === 'red') {
redCounter++
} else if (randomColor === 'blue') {
blueCounter++
} else if (randomColor === 'black') {
blackCounter++
}
var square = {
color: randomColor,
word: wordList[randomWordNum],
}
coloredWords.push(square)
console.log(square)
}
//Loops through above function until the array is x values
const wordArr = () => {
while (coloredWords.length < 9 ){
createSquare()
}
}
wordArr()
console.log(coloredWords)
You can first create an array of all colors 3 times (eg ['red', 'blue', 'black', 'red', 'blue', 'black', 'red', 'blue', 'black']), then shuffle it, and then on each iteration, select and remove an element from the array:
//Globals
const wordList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
const colorList = ['red', 'blue', 'black']
const coloredWords = [];
const COUNT_FOR_ONE_COLOR = 3;
const randomColorsArr = Array.from(
{ length: COUNT_FOR_ONE_COLOR * colorList.length },
(_, i) => colorList[i % colorList.length]
);
// Shuffle:
// https://stackoverflow.com/a/12646864
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
shuffleArray(randomColorsArr);
const createSquare = () => {
const randomWordNum = Math.floor(Math.random() * (wordList.length))
const randomColor = randomColorsArr.pop();
const square = {
color: randomColor,
word: wordList[randomWordNum],
}
coloredWords.push(square)
};
for (let i = 0; i < 9; i++) {
createSquare();
}
console.log(coloredWords)
I'm using ApexCharts to display data from a database, and it works perfectly fine until I have to create multiple data series. I get the data in the form of multiple arrays like so (nb: I don't get a list of fruits, it's for the sake of example)
var fruits = ["apple","apple","apple","apple","apple"]; // Name of the fruit
var dates = ["2019-04-01","2019-04-02","2019-04-03","2019-04-04","2019-04-05"];
var values = [14,9,17,5,11];
Where fruits[0] goes with dates[0] and values[0] (think of them as row in a database).
In this case I have only one data series to pass to ApexCharts and I can do it like this :
var matchedData = [];
// I create nested arrays in matchedData, each array corresponding to a tuple, like so : matched[0] = [dates[0],values[0]]
for(let i = 0; i < fruits.length; i++) {
date = new Date(dates[i]); // To transform the date string to Javascript timestamp
matchedData[i] = [date.getTime(),values[i]];
}
var options = {
chart: {
type: 'line'
},
series: [{
label: "Data",
data: matchedData
}],
xaxis: {
type: 'datetime'
},
}
var chart = new ApexCharts(document.getElementById('chart'), options);
chart.render();
But it only works when the "fruits" array contains solely one kind of fruit and not different kinds. So if my arrays are like this :
var fruits = ["apple","banana","apple","banana","strawberry"];
var dates = ["2019-04-01","2019-04-02","2019-04-03","2019-04-04","2019-04-05"];
var values = [14,9,17,5,11];
I have to dynamically create 3 arrays named each after a fruit (so in this case, "apple", "banana" and "strawberry"), each of them containing the corresponding date and value. Each array would look like this :
apple = [
["2019-04-01", 14], // I wrote the time in string for readability here but in reality it would be written in the JavaScript timestamp format
["2019-04-03", 17]
];
banana = [
["2019-04-02", 9],
["2019-04-04", 5]
];
strawberry = [
["2019-04-05",11]
];
And these data series would be passed to ApexCharts like this :
var options = {
chart: {
type: 'line'
},
series: [
{
label: "apple",
data: apple
},{
label: "banana",
data: banana
},{
label: "strawberry",
data: strawberry
}
],
xaxis: {
type: 'datetime'
},
}
var chart = new ApexCharts(document.getElementById('chart'), options);
chart.render();
My problem is the following :
I don't know how many series I will need to create until runtime, I don't know their name, and I don't know either how to pass them to ApexCharts dynamically (if there are N series, I need N {label/data} clauses).
I've got the start of a solution for dynamically creating an array for each fruit, it looks like this :
for(let i = 0; i < fruits.length; i++) {
if(fruits[i] in window) {
date = new Date(dates[i]);
window[fruits[i]].push([date.getTime(),values[i]]);
} else {
window[lines[i]] = [];
date = new Date(dates[i]);
window[lines[i]].push([date.getTime(),values[i]]);
}
}
And I can then later access each array but only if already know the different values there are in the "fruits" array.
You can use the following approach for grouping by fruit.
var fruits = ["apple","banana","apple","banana","strawberry"];
var dates = ["2019-04-01","2019-04-02","2019-04-03","2019-04-04","2019-04-05"];
var values = [14,9,17,5,11];
let result = fruits.reduce((a, f, i) => {
(a.result[f] || (a.result[f] = [])).push([a.dates[i], a.values[i]]);
return a;
}, {dates, values, result: {}}).result;
// now, you can get the apple info as follow:
console.log(JSON.stringify(result["apple"], null, 1));
console.log(JSON.stringify(result, null, 1));
.as-console-wrapper { min-height: 100%; }
I found myself. I used the window global object like I said earlier like so :
for(let i = 0; i < fruits.length; i++) {
if(!(fruits[i] in window)) {
window[fruits[i]] = [];
}
date = new Date(dates[i]);
window[fruits[i]].push([date.getTime(),values[i]]);
}
Then I made an array listing each unique name from the "fruits" array by using this function :
var fruitsName = fruits.unique();
Array.prototype.unique = function() {
return this.filter(function (value, index, self) {
return self.indexOf(value) === index;
});
}
And then I iterated on the unique names array to add each series to my chart from ApexCharts using the appendSeries() method like this :
for(let i = 0; i < fruitsName.length; i++) {
chart.appendSeries({
name: fruitsName[i],
data: window[fruitsName[i]]
});
}
This is what code looks like:
var figury = new Array("kwadrat", "kolo", "trojkat_rownoboczny", "trojkat_prostokatny", "heksagon", "trapez");
var kolory = new Array("blue", "green", "red", "yellow");
var losowanie_figur = function(wylosowane_figury) {
for(var i = 0; i <= 3; i++)
wylosowane[i] = { figura: figury[losowe[i]] };
}
var losowanie_kolorow = function(wylosowane_kolory) {
for(var i = 0; i <= 3; i++)
wylosowane[i] = { kolor: kolory[losowe[i]] };
}
What I want to do is to update the array with the color values but what I get is that the previous values are being overwritten. I'm using a random f
This is what I get:
{ '0': { kolor: 'red' },
'1': { kolor: 'green' },
'2': { kolor: 'yellow' },
'3': { kolor: 'blue' } }
And what I really wanted is:
{ '0': { figura: 'kwadrat', kolor: 'green' },
'1': { figura: 'heksagon', kolor: 'red' },
'2': { figura: 'trapez', kolor: 'blue' },
'3': { figura: 'trojkat_prostokatny', kolor: 'yellow' } }
You can do this:
var losowanie_kolorow = function(wylosowane_kolory){
for(var i=0;i<=3;i++)
wylosowane[i].kolor =kolory[losowe[i];
}
You need to set the properties on the objects, rather than setting the whole array value to be a new object.
As an aside you never set up wylosowane in this code, I'm assuming that was cut to simplify? You also never use the parameters of the functions. Since thats the case its probably better to not have the params at all.
Also its generally considered best practice to declare arrays as
var kolory = ["blue", "green", "red", "yellow"];
rather than
var kolory = new Array("blue", "green", "red", "yellow");
Most people consider it more readable, and it avoids some weird edge cases (new Array(3) for instance, creates an empty array of length 3, rather than an array of length 1 with 3 at the 0 index)
You can use underscore JS to achieve something similar to this: http://underscorejs.org/#zip
var figury = new Array("kwadrat", "kolo", "trojkat_rownoboczny", "trojkat_prostokatny", "heksagon", "trapez");
var kolory = new Array("blue", "green", "red", "yellow");
var combined = _.zip(figury, kolory);
Hope this helps
Sorry If this seems short/ un-researched (I've probably been looking in all the wrong places with the wrong logic in mind) but I feel like the answer is going to be simple/straight forward.
Say I have some variables set up, for example:
var path1 = paper.path("M 95, 259 L 110, 250... etc. etc.. ");
var path2 = paper.path("M 96 138 L 55, 100,... etc... etc..");
Insert them into a set...
var set1 = paper.set();
set1.push(icon1, path1);
EDIT: These two steps x2 ^^^^^^^^^
Then want to go ahead and insert that new set into a new array with new objects with their own ids, So I can later reference that array when I set up a click handler function.
Basically is there any way I should/can go about attaching each set to this new array of new objects?
EDIT:
Here's more specifically what I'm working with.. I created a for loop to run through that array... which works but it only displays the first switch DIV no matter which set is clicked. Instead I want to display switch2 when set2 is clicked... or even switch3 with set3.. etc. Thanks.
switches = [
{ id: 'switch1', set: set1 },
{ id: 'switch2', set: set2 }
],
current,
max,
i;
for (i = 0, max = switches.length; i < max; i++) {
initSwitch(switches[i].set, switches[i].id);
}
function initSwitch(switchStr, targetId) {
txElm = document.getElementById(targetId);
var clickHandler2 = (function (e) {
if (current) {
if (current === txElm) {
return;
}
current.style.display = '';
}
current = txElm;
current.style.display = 'block';
//this.toFront();
paper.safari();
});
set1.click( clickHandler2 );
set2.click( clickHandler2 );
FINAL EDIT:
check out this jsfiddle I just wrote up. I want the first box and image to display the "switch1" Div and the second two boxes to display the "switch2" Divs.
http://jsfiddle.net/thecomplex/AEa2X/13/
I think what you're looking for is something like this:
var switches = [
{ id: 'switch1', set: set1 },
{ id: 'switch2', set: set2 }
];
var switches = [
{ id: 'switch1', set: set1 },
{ id: 'switch2', set: set2 }
];
var current;
for (var i = 0, max = switches.length; i < max; i++) {
initSwitch(switches[i].set, switches[i].id);
}
function initSwitch(set, targetId) {
var txElm = document.getElementById(targetId);
set.click(function(e) {
if(current == txElm)
return;
if(current)
current.style.display = '';
current = txElm;
current.style.display = 'block';
paper.safari();
});
}