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;
}
Related
function showPrimes(n) {
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
console.log(i); // a prime
}
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
document.getElementById("res").innerHTML = showPrimes(5);
<div id='res'></div>
How to show all showprimes value into the div element it return undefined when using above code
It shows undefined because showPrimes doesn't return anything, so calling it results in the value undefined. If you want it to return something, return something (perhaps an array); then that will get assigned to innerHTML (after being converted to string). See *** comments:
function showPrimes(n) {
const primes = []; // ***
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
console.log(i);
primes.push(i); // ***
}
return primes; // ***
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
document.getElementById("res").innerHTML = showPrimes(5);
<div id="res"></div>
That uses the default conversion of array to string, which calls Array#join, which uses a comma between the values. You could call join explicitly to do something else, or any of several other tweaks.
There is small issue in your code:
You are not returning anything from showPrimes function that can be actually assigned in
document.getElementById("res").innerHTML.
If you directly return a single value from showPrimes function then it will not give you the next prime number as showPrimes function will get terminated. So, the solution would be to store the prime numbers in an array and then return this array when no prime numbers are left to generate.
var primeArray = [];
function showPrimes(n) {
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
console.log(i); // a prime
primeArray.push(i);
}
return primeArray;
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
document.getElementById("res").innerHTML = showPrimes(5);
<div id='res'></div>
I know how do closures work, but it is not very clear to me.
How does the below snippet works under the hood (output's 0) :
function fillFunctionArr() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = {
inner: i,
innerFunc: function() {
console.log(this.inner)
}
}
}
return arr;
}
var ex = fillFunctionArr();
ex[0].innerFunc()
Sure, if it were declared as :
arr[i] = {
// inner: i,
innerFunc: function() {
console.log(i)
}
}
The output will be 10.
Why is the first snippet more preferable than the below one:
function fillFunctionArr() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = (function(qnt) {
return function() {
console.log(qnt)
}
})(i);
}
return arr;
}
var ex = fillFunctionArr();
ex[0]()
There is no 'preferable' choice. The choice depends on logic of outer code, that you want to extend. Both initialized immediately on cycle iteration. It's just one of the edges of JS flexibility.
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;
}
This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?
Question from Object-Oriented JavaScript book: Imagine Array() doesn't exist and the array literal notation doesn't exist either. Create a constructor called MyArray() that behaves as close to Array() as possible.
I thought it would be a good challenge to test my skills. This is what I came up with, but it doesn't work and is very incomplete.. I am a bit stumped:
function MyArray(){
// PRIVATE FIELDS -----------------------
var initialData = arguments;
var storage;
// PRIVATE METHODS ----------------------
function refresh(){ //this doesn't work :(
for(var i = 0; i < storage.length; i++){
this[i] = storage[i]
}
};
function initialize(){
storage = initialData;
refresh();
}
function count(){
var result = 0;
for(var item in this){
//console.log(item, parseInt(item), typeof item);
if(typeof item == 'number'){
result++;
}
}
return result;
};
initialize();
// PUBLIC FIELDS -------------------------
this.length = count();
// PUBLIC METHODS ------------------------
//todo:
this.push = function(item){
refresh();
}
this.pop = function(){}
this.join = function(){}
this.toString = function(){}
}
var c = new MyArray(32,132,11);
console.log(c, c.length);
This isn't for any production code or any project.. just to try to learn JavaScript a lot more. Can anyone try to help me with this code?
The thing is that you can use arguments object. It's not an array that was created with Array() so you won't break rules of the exercise. Here's what you need to do:
this.length = 0;
for (var i in arguments) {
this[this.length] = arguments[i];
this.length++;
}
I forgot to mention that ANY object is an associative array, so it's not a wrong thing to apply associative arrays for the exercise as we don't use the Array() object itself.
To author of the question: in your example you use: this["i"] = storage[i] that equals to this.i = storage[i]. Try to remove quotes and use it like this[i] = storage[i]
for(var item in this){
if(typeof item == 'number')
A property name is always a string. You'll need to check if it is the string representation of a number from 0 to MaxArrayLength. You could for example do
for (var i=0; i<4294967296; i++)
if (i in this)
result = i;
You might also be interested in these articles or the official specification for Array behaviour.
I'm currently looking through this book at the moment, I probably should try something more recent but its not that out of date yet and the principals are still sound...well they are in my opinion.
Anyway I went for a slightly different solution although I did take inspiration from the op in how he is handling strings. I think the challenge of this exercise is not to create any more arrays otherwise...its a bit of a crazy challenge, especially for people new to the language.
var MyArray = function () {
var args = arguments;
var length = 0;
for each(var item in args) {
this[length++] = item;
}
this.toString = function () {
var result = args[0];
for (var i = 1; i < args.length; i++) {
result += ',' + args[i];
}
return result;
}
this.length = length;
this.push = function (push) {
var newLength = args.length++;
args[newLength] = push;
this[newLength] = push;
return ++length;
}
this.pop = function () {
delete args[--args.length];
delete this[args.length];
length--;
return args;
}
this.join = function(joiner){
if(typeof arguments[0] === "undefined"){
joiner = ',';
}
var result = args[0];
for (var i = 1; i < args.length; i++) {
result += joiner + args[i];
}
return result;
}
}
var a = new MyArray(1, 2, 3, 'test');
console.log(a.toString());
console.log(a[a.length - 1]);
console.log(a.push('boo'));
console.log(a.toString());
console.log(a.pop());
console.log(a.toString());
console.log(a.join(','));
a.join(' isn\'t ');
My solution is:
function MyArray() {
this.length = 0;
for(i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
this.length++;
}
this.toString = function(joiner = ',') {
let str = this[0] ? this[0] : '';
for(i=1;i<this.length; i++) {
str+= joiner + this[i];
}
return str;
};
this.push = function(value) {
this[this.length++] = value;
return this.length;
};
this.pop = function() {
let value = this[this.length -1];
delete this[--this.length]
return value;
};
this.join = function(joiner) {
return this.toString(joiner);
}
}