Best practice to multiple 'ifs' - Javascript - javascript

I built a function to make a responsive carousel with multiples imgs per slide. (couldn't make Owl Carousel work on my Angular project, but not the point here).
I set the amount of img that will be presented by slide based on the current screen width.
Here is my code:
imgsHistoria = [
"../../assets/imgs/historia/hist_01.png",
"../../assets/imgs/historia/hist_02.png",
"../../assets/imgs/historia/hist_03.png",
"../../assets/imgs/historia/hist_04.png",
"../../assets/imgs/historia/hist_05.png",
"../../assets/imgs/historia/hist_06.png",
"../../assets/imgs/historia/hist_07.png",
"../../assets/imgs/historia/hist_08.png",
"../../assets/imgs/historia/hist_09.png",
"../../assets/imgs/historia/hist_10.png",
];
imgsHistoriaArray = [];
resizeCarousel() {
let images = this.imgsHistory;
let cut = this.getCut();
this.imgsHistoryArray = [];
for (var i = 0; i < images.length; i = i + cut) {
this.imgsHistoryArray.push(images.slice(i, i + cut));
}
}
getCut() {
if (this.getScreenWidth < 480) {
return 1
} if (this.getScreenWidth < 576) {
return 2
} if (this.getScreenWidth < 768) {
return 3
} if (this.getScreenWidth < 992) {
return 4
}
return 6;
}
The thing is that I have CodeMetrics installed and it's showing that the getCut() function has complexity 10, which is not great. How can I improve this function?

You could use an array and a loop to reduce the number of if statements:
getCut() {
let widths = [480, 576, 768, 992];
for(let i = 0; i < widths.length; i++) {
let width = widths[i];
if(this.getScreenWidth < width)
return i+1;
}
return 6;
}
Here we define an array containing the widths, loop through until we find the correct value, then return the corresponding number based on its index.

Related

Optimizing updating many elements continuously with react

This is my first time working with react, and i tried to create an animated background. I did this by creating a large amount of divs, and updating their position every time a timer ticks.
moveCircles() {
let tempCircles = this.state.bgCircles;
for (let i = 0; i < amount; i++) {
tempCircles[i].left = this.state.bgCircles[i].left + Math.cos(this.state.bgCircles[i].angle) * 2;
tempCircles[i].top = this.state.bgCircles[i].top + Math.sin(this.state.bgCircles[i].angle) * 2;
if (tempCircles[i].top < (0 - tempCircles[i].radius)) {
tempCircles[i].top = totalHeight;
} else if(tempCircles[i].top > totalHeight) {
tempCircles[i].top = 0 - tempCircles[i].radius
}
if (tempCircles[i].left < (0 - tempCircles[i].radius)) {
tempCircles[i].left = totalWidth;
} else if (tempCircles[i].left > totalWidth) {
tempCircles[i].left = 0 - tempCircles[i].radius;
}
}
this.setState({
bgCircles: tempCircles,
initialized: true
});
}
However, this is (unsurprisingly) not very optimized, and can get a bit laggy.
My latest try looks like this: https://codepen.io/Miasha/pen/XWqZVeV
Is there a more optimized way of achieving this?

loop through elements and set CSS styling at same time

So I want to loop through elements with a class and then loop the individual element and gradually decrease the "left" css property.
let move = document.getElementsByClassName("move");
for (var i = 0; i < move.length; i++) {
const left = move[i].getBoundingClientRect().left;
const elementId = move[i].id;
for (let j = left; j > -20; j--) {
document.getElementById(elementId).style.left = j + "%";
await new Promise(resolve => setTimeout(resolve, 20));
}
}
However, I'm using "await" to delay so that it moves slowly and doesn't zip across the screen. I want it so that all the elements have their CSS left property decrease at the same time. But instead, because of the await, the first element increases its left property and when its left property reaches the end, only then the next element goes. Please advise
While I think it would be better to use pure CSS transitions/animations for this, you could use a while loop to iterate over the elements with the move class, subtracting 1% from their left value until they are completely off screen (right <= 0).
const start = async () => {
const move = document.getElementsByClassName("move");
const canMoveLeft = () => {
const move = document.getElementsByClassName("move");
for (let i = 0; i < move.length; i++) {
if (move[i].getBoundingClientRect().right > 0) return true;
}
return false;
};
while (canMoveLeft()) {
for (let i = 0; i < move.length; i++) {
const { left, right } = move[i].getBoundingClientRect();
if (right > 0) {
const windowWidth = window.innerWidth;
const leftVal = `${Math.round((left / windowWidth) * 100) - 1}%`;
document.getElementById(move[i].id).style.left = leftVal;
}
}
await new Promise((resolve) => setTimeout(resolve, 20));
}
};
From what I understood, you want to animate left property of elements with class "move".
If that's the case you can do:
1st:
add transition property in move class
.move {
transition: left 2s;
}
2nd:
let move = document.getElementsByClassName("move");
for(let item of move) {
item.style.left = "-20%";
}

How do you prevent a checkerboard getting our of view when changing the resolution?

So I'm currently working on a project that requires a grid/checkerboard. I've already made my grid in Javascript and I've also managed to center my grid.
The problem I'm having is that when I change my resolution with the device toolbar, the grid gets out of view. My goal is to make the whole grid visible no matter what phone or computer I use.
I would appreciate the help from you guys!
This is how I'm making my grid.
for (i = 0; i < row; i++) {
grid[i] = new Array(col);
}
//making a spot for every grid.
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
grid[i][j] = new Spot(i, j);
}
}
This is how I'm drawing the grid.
function Spot(i, j) {
this.x = i;
this.y = j;
this.show = function (color) {
fill(color);
rect(this.x * w, this.y * h, w - 1, h - 1);
}
}
w is the width of ONE single grid and h is the height of ONE single grid. col and row are the amount of rows and columns I want the grid to have.
I also call the function "this.show" every frame. The result looks like this:
The grid that is out of view
you need to check against the media type:
var l_strMobileMedia = '(max-width:320px)'
var mqList = window.matchMedia(l_strMobileMedia);
if(!mqList.matches){
l_strMobileMedia = '(max-width:481px)'
mqList = window.matchMedia(l_strMobileMedia);
}
if(!mqList.matches){
l_strMobileMedia = '(max-width:768px)'
mqList = window.matchMedia(l_strMobileMedia);
}
if(s_bIsMobile == null || s_bIsMobile != mqList.matches){
Session.set_mobile_layout(mqList.matches);
}
s_bIsMobile = mqList.matches;
return mqList.matches;
I hope this gets you started. If you want to do it in realtime, you need to hook to window resize event or set a timer that does the queries for you.

Getting new colors randomly with loop

I am trying to randomize colors by generating random number, then applying
it to array to get an color array containing font-color and background-color.
At every "skill" I want to have unique color scheme. So each time I loop skill array I loop color array to fetch color scheme. If this color scheme number (which is same as the randomNumber) is already in use I random again. I do this with do/while loop. When color is not found it pushes it to usedColors array and paints the picture.
For some reason I am still getting same colors. I pasted two pictures to the bottom. Console.log image is about usedColors array (the randomly generated numbers)
var usedColors = [];
$.each(knowledges, (i, knowledge) => {
do {
var r = Math.floor(Math.random() * Math.floor(colors.length)),
rColors = colors[r];
} while ($.inArray(r, usedColors) == 0);
usedColors.push(r);
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": rColors[0], "color": rColors[1]})
);
});
inArray gives position of the matching element. So compare against -1, to know that element is not present in the usedColors array.
var usedColors = [];
$.each(knowledges, (i, knowledge) => {
do {
var r = Math.floor(Math.random() * Math.floor(colors.length)),
rColors = colors[r];
} while ($.inArray(r, usedColors) != -1);
usedColors.push(r);
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": rColors[0], "color": rColors[1]})
);
});
To generate array of unique numbers from certain interval you can do this.
In your case the range will be 0, arr.length - 1.
// function that will generate random unique random numbers between start
// and end, and store already generated numbers in closure
function generateUniqueRandom(start, end) {
const used = [];
function generateInner() {
let r;
while (!r) {
r = Math.floor(Math.random() * (end - start) + 1) + start;
if (used.includes(r)) {
r = null;
} else {
used.push(r);
}
}
return r;
}
return generateInner;
}
const random1 = generateUniqueRandom(0, 20);
const nums1 = [];
for (let i = 0; i < 10; i++) {
nums1.push(random1());
}
console.log(nums1);
const random2 = generateUniqueRandom(0, 20);
const nums2 = [];
for (let i = 0; i < 20; i++) {
nums2.push(random2());
}
console.log(nums2);
But you need to be careful not to generate more numbers that the specified range is, otherwise you will be stuck in an infinite loop.
In your while loop, are you checking if the array is unique? If so, it looks like you may not be using $.inArray correctly.
Put this in your while loop:$.inArray(r, usedColors) !== -1
jQuery.inArray(), how to use it right?
I think your loop method has many interactions, I mean your loop is traveling so much that it only ends until you find the random number that is not in the array (A short performance problem). An alternative method so that the array elements are random:
function shuffleArray(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
const colors = [["black","green"], ["white","blue"], ["pink","white"]];
let usedColors = shuffleArray(colors);
//You can now do this:
$.each(knowledges, (i, knowledge) => {
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": usedColors[i][0], "color": usedColors[i][1]})
);
});

Javascript challenge - which basket contains the last apple?

I'm presented with the following challenge question:
There are a circle of 100 baskets in a room; the baskets are numbered
in sequence from 1 to 100 and each basket contains one apple.
Eventually, the apple in basket 1 will be removed but the apple in
basket 2 will be skipped. Then the apple in basket 3 will be removed.
This will continue (moving around the circle, removing an apple from a
basket, skipping the next) until only one apple in a basket remains.
Write some code to determine in which basket the remaining apple is
in.
I concluded that basket 100 will contain the last apple and here's my code:
var allApples = [];
var apples = [];
var j = 0;
var max = 100;
var o ='';
while (j < max) {
o += ++j;
allApples.push(j);
}
var apples = allApples.filter(function(val) {
return 0 == val % 2;
});
while (apples.length > 1) {
for (i = 0; i < apples.length; i += 2) {
apples.splice(i, 1);
}
}
console.log(apples);
My question is: did I do this correctly? What concerns me is the description of "a circle" of baskets. I'm not sure this is relevant at all to how I code my solution. And would the basket in which the remaining apple reside be one that would otherwise be skipped?
I hope someone can let me know if I answered this correctly, answered it partially correct or my answer is entirely wrong. Thanks for the help.
So, ... I got WAY too into this question :)
I broke out the input/output of my last answer and that revealed a pretty simple pattern.
Basically, if the total number of items is a power of 2, then it will be the last item. An additional item after that will make the second item the last item. Each additional item after that will increase the last item by 2, until you reach another item count that is again divisible by a power of 2. Rinse and repeat.
Still not a one-liner, but will be much faster than my previous answer. This will not work for 1 item.
var items = 100;
function greatestPowDivisor(n, p) {
var i = 1;
while(n - Math.pow(p, i) > 0) {
i++;
}
return Math.pow(p, (i - 1));
}
var d = greatestPowDivisor(items, 2)
var last_item = (items - d) * 2;
I believe Colin DeClue is right that there is a single statement that will solve this pattern. I would be really interested to know that answer.
Here is my brute force solution. Instead of moving items ("apples") from their original container ("basket") into a discard pile, I am simply changing the container values from true or false to indicate that an item is no longer present.
var items = 100;
var containers = [];
// Just building the array of containers
for(i=0; i<items; i++) {
containers.push(true);
}
// count all containers with value of true
function countItemsLeft(containers) {
total = 0;
for(i=0; i<containers.length; i++) {
if(containers[i]) {
total++;
}
}
return total;
}
// what is the index of the first container
// with a value of true - hopefully there's only one
function getLastItem(containers) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
return(i);
}
}
// shouldn't get here if the while loop did it's job
return false;
}
var skip = false;
// loop through the items,
// setting every other to false,
// until there is only 1 left
while(countItemsLeft(containers) > 1) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
if(skip) {
skip = false;
} else {
containers[i] = false;
skip = true;
}
}
}
}
// what's the last item? add one to account for 0 index
// to get a human readable answer
var last_item = getLastItem(containers) + 1;
Needs error checking, etc... but it should get the job done assuming items is an integer.

Categories

Resources