JavaScript function to generate random instances of Object - javascript

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)

Related

How to set different Value for each loop in IF statement

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

applying colors from array to buttons without repetition?

I'm trying to apply an array of colors
let wordsForTheGame=['red','blue','yellow','orange','green','black','aqua','gray','purple'];
exactly to 9 button elements.
I use this function to shuffle:
function shuffleArray (arr) {
for (let i = arr.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * Math.floor(arr.length - 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
and this to append colors to buttons:
batons.forEach(btn => {
btn.style.backgroundColor = shuffleArray(wordsForTheGame)[0];
});
Yet, the colors are still repeating.
How can I make this function append colors to all the buttons , so they won't repeat and there will be no duplicated color on 2 or more buttons ?
You are shuffling the array again for each button. Shuffle it just once on the beginning and then use the same shuffled array for all the buttons.
const shuffledArray = shuffleArray(wordsForTheGam);
Object.keys(batons).forEach(key => {
batons[key].style.backgroundColor = shuffledArray[key];
});
let wordsForTheGame = ['red', 'blue', 'yellow', 'orange', 'green', 'black', 'aqua', 'gray', 'purple'];
let alreadyUsed = [];
const randomColor = () => {
return wordsForTheGame.filter(word => !alreadyUsed.includes(word))[Math.floor(Math.random() * items.length)];
}
batons.forEach(btn => {
const color = randomColor();
alreadyUsed.push(color)
btn.style.backgroundColor = color;
});

frequentVowelCounter(word, count = {}) using recursion

I can do this problem using a for loop, but I'm having trouble using the recursive method.
I want to count how many times each vowel is called and return the most frequent vowel in an array.
If the array does not have a vowel to return is a string.
const vowel = ['a', 'e', 'i', 'o', 'u'];
frequentVowelCounter(word, count = {})
frequentVowelCounter(['cat', 'rain', 'dorm', 'apple', 'sun'])); // 'a'
I tried writing a base case:
if (word.length != vowel)
return ''
I don't think it's right, I'm just stuck.
Why would you want to make this recursive? An iterative approach works well.
You will need to loop over each word, and for each word, its letters. As you loop over each letter, you check to see if it is a vowel. If it is a vowel, you increment the frequency map i.e byLetter.
After you build the letter frequency map, you will need to invert the map into a count map i.e. byCount.
Finally, you can get the keys of the byCount map to find the max count and return the value for the corresponding entry in the map.
Note: You may have more than one letter share the same max occurrence count. This is why the result of the function in an array.
const maxFrequency = (words, letters) => {
const
letterSet = new Set(letters),
byLetter = new Map(),
byCount = new Map();
for (let word of words) {
for (let letter of word.toLowerCase().split('')) {
if (letterSet.has(letter)) {
byLetter.set(letter, (byLetter.get(letter) ?? 0) + 1);
}
}
}
for (let [letter, count] of byLetter) {
const letters = byCount.get(count) ?? new Set();
byCount.set(count, letters.add(letter));
}
return [...byCount.get(Math.max(...byCount.keys()))];
};
const
vowels = ['a', 'e', 'i', 'o', 'u'],
words = ['cat', 'rain', 'dorm', 'apple', 'sun'],
mostFrequent = maxFrequency(words, vowels);
console.log(...mostFrequent); // ['a']
If you want to do this recursively, just join all the words together and iterate through the string starting at index 0. The helper function i.e. __helperFn should never be called directly.
Note: You will need a helper function to set up and perform the recursion.
const __helperFn = (str, letterSet, frequency) => {
if (str.length === 0) {
const max = Math.max(...Object.values(frequency));
return Object.entries(frequency)
.filter(([letter, count]) => count === max)
.map(([letter]) => letter);
}
const letter = str.charAt(0);
if (letterSet.has(letter)) {
frequency[letter] = (frequency[letter] ?? 0) + 1;
}
return __helperFn(str.slice(1), letterSet, frequency);
}
const maxFrequency = (words, letters) =>
__helperFn(words.join('').toLowerCase(), new Set(letters), {});
const
vowels = ['a', 'e', 'i', 'o', 'u'],
words = ['cat', 'rain', 'dorm', 'apple', 'sun'],
mostFrequent = maxFrequency(words, vowels);
console.log(...mostFrequent); // ['a']
This should work:
let w = ["cat", "rain", "dorm", "apple", "sun"];
const vowelCounter = (words) => {
let vowels = {
a: 0,
e: 0,
i: 0,
o: 0,
u: 0,
};
const count = (char) => {
if (vowels.hasOwnProperty(char)) {
vowels[char]++;
}
};
const rekursive = (words, index = 0) => {
if (index === words.length) {
return;
}
let i = 0;
while (true) {
if (i >= words[index].length) {
break;
}
try {
count(words[index].charAt(i));
} catch (error) {}
i++;
}
rekursive(words, ++index);
};
rekursive(words);
console.log(vowels);
};
vowelCounter(w);

how can I use includes() function on nested arrays?

I have an array like this:
var arr = [];
arr = [['red', 685], ['green', 210], ['blue', 65]];
Also I have two variables:
var color = 'blue';
var number = 21;
All I'm trying to do is checking the first item of each nested array of arr and then either update the second item of it or make a new array for it.
Here is some examples:
Input:
var color = 'blue';
var number = 21;
Expected output:
arr = [['red', 685], ['green', 210], ['blue', 21]];
Input:
var color = 'yellow';
var number = 245;
Expected output:
arr = [['red', 685], ['green', 210], ['blue', 21], ['yellow', 245]];
Here is what I've tried so far:
if ( !arr.includes(color) ) {
arr.push([color, number]);
} else {
arr[color] = time;
}
But !arr.includes(color) condition is wrong. Because each item of arr is also an array. Anyway, how can I use includes() function on the first item of nested arrays?
You cannot directly use includes on nested array, however, you can use find on array.
arr.find(el => el[0] === color)
This will return the element of array found else undefined. The returned value can be used to update the second element in the array.
var arr = [
['red', 685],
['green', 210],
['blue', 65]
];
var color = 'blue';
var number = 21;
function upsert(array, color, number) {
var exists = arr.find(el => el[0] === color);
if (exists) {
exists[1] = number;
} else {
arr.push([color, number]);
}
}
upsert(arr, color, number);
console.log(arr);
var color = 'yellow';
var number = 245;
upsert(arr, color, number);
console.log(arr);
Simply iterate the array and update the value if found, else push a new value
Demo
var arr = [['red', 685], ['green', 210], ['blue', 65]];
console.log(updateArray(arr, 'blue', 21));
function updateArray(arr, color, value)
{
var isFound = false;
arr = arr.map( function(item){
if( item[0] == color )
{
isFound = true;
item[1] = value;
}
return item;
});
if ( !isFound )
{
arr.push([color, value]);
}
return arr;
}
You should make a loop that cycles through the array because, as you pointed out yourself, each element of the array is itself an array.
If you do:
for(let i = 0; i < arr.length; i++){
if ( !arr[i].includes(color) ) {
arr.push([color, number]);
} else {
arr[i][1] = time;
}
}
This way you are checking if the array at position i has the color, if it doesn't you push a new array into the array, otherwise you change the array value at index 1 of the array i

How to implement cartesian product of n array with different lenghts in javascript? [duplicate]

This question already has answers here:
Cartesian product of multiple arrays in JavaScript
(35 answers)
Closed 8 years ago.
let's say that i have the following array:
data[size] = Array('s', 'm');
data[color] = Array('blue', 'red');
I need a function that outputs me the following array
variations[] = Array('s', 'blue');
variations[] = Array('s', 'red');
variations[] = Array('m', 'blue');
variations[] = Array('m', 'red');
The function must work also on arrays like:
data[size] = Array('s', 'm', 'l');
data[color] = Array('blue', 'red');
data[version] = Array('run', 'walk');
It should give an array like
variations[] = Array('s', 'blue', 'run');
variations[] = Array('s', 'blue', 'walk');
variations[] = Array('s', 'red', 'run');
variations[] = Array('s', 'red', 'walk');
And so on ...
How can i implement it?
p.s.: if there is a specific name for this problem let me know so i'll edit the question title for future use
The following works having n arrays:
function cartesianProduct(data) {
var current = [[]];
for (var p in data) {
var arr = data[p];
var newCurrent = [];
for (var c = 0; c < current.length; c++) {
var baseArray = current[c];
for (var a = 0; a < arr.length; a++) {
var clone = baseArray.slice();
clone.push(arr[a]);
newCurrent.push(clone);
}
}
current = newCurrent;
}
return current;
}
for the following data:
var data = {
size: ['s', 'm'],
color: ['blue', 'red'],
version: ['run', 'walk', 'jump']
};
will return all the combinations:
var variations = [
["s","blue","run"],
["s","blue","walk"],
["s","blue","jump"],
["s","red","run"],
["s","red","walk"],
["s","red","jump"],
["m","blue","run"],
["m","blue","walk"],
["m","blue","jump"],
["m","red","run"],
["m","red","walk"],
["m","red","jump"]
];
Your syntax is no valid JS, it seems PHP.
I assume you have
var data = {
size: ['s', 'm'],
color: ['blue', 'red']
};
And you want
var variations = [
['s', 'blue'],
['s', 'red'],
['m', 'blue'],
['m', 'red']
];
To get that, you can use
var variations = [];
for (var i=0; i<data.size.length; ++i)
for (var j=0; j<data.color.length; ++j)
variations.push([data.size[i], data.color[j]]);
Tried something fancy, working on local :
var variations = []
var str = "";
var ind = 0;
index = [];
for (var k in data)
{
index[index.length] = k;
str+="for(var i"+ind+" in data["+k+"])";
ind++;
}
str += "{variations[variations.length] = [";
for (;ind >= 0; ind--)
{
str+="data["+index[ind]+"][i"+ind+"]";
if (ind > 0) str += ",";
else str+="];";
}
str+= "}";
eval(str);
This allows to have any number of arrays in data =)

Categories

Resources