Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I was wondering the best way to write the code.
Scenario 1:
function main() {
var arr = [];
performSomeLogic(arr);
}
function performSomeLogic(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
// some more logic
}
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
// some other logic
}
}
scenario 2:
function main() {
var arr = [];
performSomeLogic(arr);
}
function performSomeLogic(arr) {
var sum = 0;
sum = commonLoop(true, arr);
sum = commonLoop(false, arr);
}
function commonLoop(flag, arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
if(flag){
// some more logic
} else {
// some other logic
}
}
return sum;
}
As you can see in the first scenario we have only 2 functions (main and performSomeLogic) and in the second scenario we have 3 functions and the code has been modularised (main, performSomeLogic and commonLoop).
Which way of coding is better?
I think that a method should do a single thing where possible. Keep your methods so that you can convey better meaning to the individual steps.
I feel this makes your main method a lot cleaner and more readable, though admittedly It's probably a lot more verbose.
function main() {
var arr = [];
var sum;
sum += performFirstLogic(arr);
sum += performSecondLogic(arr);
}
function performFirstLogic(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
// some more logic
}
return sum;
}
function performSecondLogic(arr) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
// some more logic
}
return sum;
}
Depending on the circumstances, I may find the opportunity to pass functions around to make things easier.
function main() {
var arr = [];
var sum;
sum += processLoop(arr, firstLogic);
sum += processLoop(arr, secondLogic);
}
function processLoop(arr, customLogic) {
var sum = 0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
customLogic(arr[i]);
}
return sum;
}
function firstLogic(data) {
//First lot of logic
}
function secondLogic(data) {
//First lot of logic
}
Your milage may vary.
Short Answer: Depends
Long Answer:
What does the code do? Is the logic occuring in the loop tightly related? Is it a separate task altogether?
Consider the following version:
Scenario 1 (Changed)
function main() {
var arr = [];
performSomeLogic(arr);
}
function performSomeLogic(distinctData, uniqueData) {
var sum = 0;
sum += clearlyPerformDistinctTask(distinctData);
sum += clearlyPerformUniqueTask(uniqueData);
}
function clearlyPerformDistinctTask(arr) {
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
// some more logic
}
return sum;
}
function clearlyPerformUniqueTask(arr) {
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
// some more logic
}
return sum;
}
It's all about READABILITY! Break smaller tasks into functions that say EXACTLY and only what they do. Its better to move distinct logic into its own neat little box, don't clutter your code! Everything can't be one giant procedure!
If two loops perform totally different tasks, don't lump them together and switch between parameters if its not clear and related logic!
However, if the logic tightly related:
Scenario 2 (Changed)
function main() {
var arr = [];
performSomeLogic(arr);
}
function performSomeLogic(data) {
var sum = 0;
sum += clearlyPerformTaskWithOptions(data, { sumType: "X"});
sum += clearlyPerformTaskWithOptions(data, { sumType: "Y"});
}
function clearlyPerformTask(arr, options) {
for (var i = 0; i < arr.length; i++) {
if (options.sumType == "X")
sum += arr[i];
else if (options.sumType == "Y")
sum += 2 * arr[i];
else
sum -= 4;
// some more logic
}
return sum;
}
Does it make sense to group your logic together? Is the data set used the same? Does it acheive the same role but by different means? Does it make sense when you read it?
These are all questions you must consider when organizing your code.
Your question is a little confusing because it's unclear what you are trying to do and why, but you might find some use for the reduce method on arrays here:
function main() {
var arr = [];
var sum = arr.reduce(sumAndDoLogic, 0);
// do stuff with sum
}
function sumAndDoLogic(sum, currentValue) {
if (/* something about currentValue */) {
// do some logic
} else {
// do some different logic
}
return sum + currentValue;
}
Related
I am developing a large javascript application and unsurprisingly in IE11 it really struggles (Chrome = 8 secs, nodejs= 8 secs, IE11 = 35 secs).
So I did some profiling and found that this method is my time sink. I have already made all the changes I could think of - is there any other performance improvement modification I can put in place?
const flatten = function(arr, result) {
if (!Array.isArray(arr)) {
return [arr];
}
if(!result){
result = [];
}
for (let i = 0, length = arr.length; i < length; i++) {
const value = arr[i];
if (Array.isArray(value)) {
flatten(value, result);
}
else {
result.push(value);
}
}
return result;
};
The method gets called lots of times, with smallish arrays (up to 10 string items, no more than 2 level deep).
Doing the if (!result) and Array.isArray(value) checks repeatedly should be avoided. I'd go for
function flatten(arr, result = []) {
if (Array.isArray(arr)) {
for (var i = 0; i < arr.length; i++) {
flatten(arr[i], result);
}
} else {
result.push(arr);
}
return result;
}
for simplicity and if the compiler doesn't optimise this enough by inlining and recognising loop patterns, I'd also try
function flatten(val) {
if (Array.isArray(val)) // omit this check if you know that `flatten` is called with arrays only
return flattenOnto(val, []);
else
return [val];
}
function flattenOnto(arr, result) {
for (var i = 0, len = arr.length; i < len; i++) {
var val = arr[i];
if (Array.isArray(val))
flattenOnto(val, result);
else
result.push(val);
}
return result;
}
I also used normal var instead of let because it had been known to be faster, dunno whether that has changed by now.
If, as you say, you also know that your arrays have a limited depth, you might even want to try to inline the recursive calls and spell it out to
function flatten(val) {
if (!Array.isArray(val)) return [val]; // omit this check if you can
var result = [];
for (var i = 0, ilen = arr.length; i < ilen; i++) {
var val = arr[i];
if (Array.isArray(val)) {
for (var j = 0, jlen = val.length; j < jlen; j++) {
// as deep as you need it
result.push(val[j]);
}
} else {
result.push(val);
}
}
return result;
}
The way you use recursion looks a bit odd to me: you're both returning the array and mutating a parameter depending on the depth level. You also have duplicated Array.isArray(array) calls. I think this code can be quite simplified, for example to something like the following (no parameter mutation as you can see):
const flatten = (array) => Array.isArray(array)
? array.reduce((accumulated, value) => accumulated.concat(flatten(value)), [])
: [array];
Not sure performances will be that improved though to be honest, but it looks more elegant in my opinion - jsPerf is your friend!
I have written a function and called another function inside but my tests show that it is not time optimized. How can I make the following code faster?
function maxSum(arr, range) {
function sumAll(array1, myrange) {
var total = 0;
if (Array.isArray(myrange)) {
for (var i = myrange[0]; i <= myrange[1]; i++) {
total += array1[i];
}
return total;
} else return array1[myrange];
}
var mylist = [];
var l = range.length;
for (var n = 0; n < l; n++) {
mylist.push(sumAll(arr, range[n]));
}
return Math.max.apply(null, mylist);
}
Algorithmic optimization: create new array with cumulative sums from index 0 to every index
cumsum[0] = 0;
for (var i = 1; i <= arr.Length; i++) {
cumsum[i] = cumsum[i-1] + arr[i-1]
Now you don't need to calculate sums for every range - just get difference
sum for range (i..j) = cumsum[j+1] - cumsum[i];
in your terms:
function sumAll(array1, myrange) {
return cumsum[myrange[1]+1] - cumsum[myrange[0]];
}
example:
arr = [1,2,3,4]
cumsum = [0,1,3,6,10]
sum for range 1..2 = 6 - 1 = 5
P.S. If your array might be updated, consider Fenwick tree data structure
1) You can define the function sumAll outside of the function maxSum because every time you call maxSum the javascript engine is recreating a fresh new function sumAll.
2) You can define myrange[1] as a variable in the initialiser part to avoid javascript to look for myrange[1] at each iteration.
for (var i = myrange[0]; i <= myrange[1]; i++) {
total += array1[i];
}
become this:
for (var i = myrange[0], len = myrange[1]; i <= len; i++) {
total += array1[i];
}
Full working code based on #MBo's excellent optimization. This passes all the tests at https://www.codewars.com/kata/the-maximum-sum-value-of-ranges-challenge-version/train/javascript, which I gather is where this problem comes from.
function maxSum(arr, ranges) {
var max = null;
var sums = [];
var sofar = 0;
for (var i = 0; i <= arr.length; i++) {
sums[i] = sofar;
sofar += arr[i];
}
for (var i = 0; i < ranges.length; i++) {
var sum = sums[ranges[i][1]+1] - sums[ranges[i][0]];
if (max === null || sum > max) {
max = sum;
}
}
return max;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm trying to create an array with random number with length of 78. I don't know how to search the entire array and check if last entry was duplicate and remove it. Someone please help.
function initAll() {
var balls = LoadInBingoBalls();
for(i=0; i < balls.length;i++)
{
(balls[i]);
}
}
function setSquare (numSquare) {
var BingoSquare = "square" + numSquare.toString();
document.getElementById(BingoSquare).innerHTML = RandomNumber(MaxNo);
}
var RandomNumber = function (max) {
return Math.floor(Math.random() * max) + 1;
}
function LoadInBingoBalls(){
var first = 0;
var last = 78;
var random;
var bag = [];
//Heres the part i get confused!
for (var i = first; i <= last; i++) {
do
bag.push(i) = setSquare(i);
while(bag.indexOf(i) !== -1)
}
return bag;
}
}
This function will create the array and check to see if it contains the number before pushing:
working example: https://jsbin.com/cabofa/1/edit?js,console
function randNum (max) {
return Math.floor(Math.random() * max) + 1;
}
function makeArray(n) {
var arr = [];
while (arr.length < n) {
var random = randNum(n);
if (arr.indexOf(random) === -1) {
arr.push(random);
}
}
return arr;
}
Create an array with all the possible values:
var possibles = [];
for(i=1; i<=78; i++) {
possibles.push(i);
}
Shuffle that array of values to be random:
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i -= 1) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
return a;
}
var shuffled = shuffle(possibles);
Now pop a value from your new array to get a random element from it:
var popped = shuffled.pop();
console.log(popped);
You can call pop() as many times as you want until the array is empty, which will return undefined. For example:
for(i=0; i<shuffled.length; i++) {
console.log(shuffled.pop());
}
I create this function in order to have a random number for my first dice game.
function rollDice() {
return Math.floor(Math.random()*6 + 1);
}
Now I want create a new function dices() with a for loop in order to use how much dices the user need.
Usually I used length in a loop as this example:
for (var i=1; i < dices.length; i++) {
// do something
}
How you got any suggestions on how to fix this?
Thank you!
I don't know if it's really what you're looking for, but you can simply create a function and replace dice.length by the number of time you want to roll, see this example:
function rollManyDice(number) {
var diceNumber = 0;
for (var i = 0; i < number; i++) {
diceNumber += rollDice();
}
return diceNumber;
}
Something like this function if I understand your question? It returns an array with the results from the number of dices thrown.
function throwDice(num) {
var arr = [];
for (var i = 0, l = num; i < l ; i++) {
arr.push(rolldice());
}
return arr;
}
throwDice(3); // e.g. [3, 5, 1]
function rollDice(sides, amount){
var results = [];
for (var i=0; i < amount; i++){
results.push( Math.floor(Math.random()*sides + 1) );
}
return results;
}
should return a array of how ever amount of dice with the specified number of sides.
I'm solving the following kata: Write a program that takes as its first argument one of the words ‘sum,’ ‘product,’ ‘mean,’ or ‘sqrt’ and for further arguments a series of numbers. The program applies the appropriate function to the series.
I have solved it (code below) but it is bulky and inefficient. I'm looking to re-write it have a single function calculator that calls the other functions (i.e. function sum, function product).
My question: how do I write the functions sum, product, sqrt, etc so when called by the function calculator, they properly take the arguments of calculator and compute the math.
Below is the bulky code:
function calculator() {
var sumTotal = 0;
var productTotal = 1;
var meanTotal = 0;
var sqrt;
if(arguments[0] === "sum") {
for(i = 1; i < arguments.length; i++) {
sumTotal += arguments[i];
}
return sumTotal;
}
if(arguments[0] === "product") {
for(i = 1; i < arguments.length; i++) {
productTotal *= arguments[i];
}
return productTotal;
}
if(arguments[0] === "mean") {
for(i = 1; i < arguments.length; i++) {
meanTotal += arguments[i];
}
return meanTotal / (arguments.length-1);
}
if(arguments[0] === "sqrt") {
sqrt = Math.sqrt(arguments[1]);
}
return sqrt;
}
calculator("sqrt", 17);
You can just create an object with the functions you need, and then have the calculator function call the correct one.
var operations = {
sum: function() { /* sum function */ },
product: function() { /* product function */ },
mean: function() { /* mean function */ },
sqrt: function() { /* sqrt function */ }
};
function calculator(operation) {
operation = operations[operation];
var args = Array.prototype.slice.call(arguments, 1);
return operation.apply(this, args);
}
You can see an example of this in action on jsFiddle.
If you don't quite understand what I'm doing in my code, I reccomend reading about call and apply in Javascript and also about objects in Javascript.
You can pass your entire arguments list to another function using the apply() method:
if(arguments[0] === "sum") {
return sum.apply(this, Array.prototype.slice.call(arguments, 1));
}
With your operations in separate methods:
function sum() {
var sumTotal = 0;
for(i = 1; i < arguments.length; i++) {
sumTotal += arguments[i];
}
return sumTotal;
}