reset a variable for a reusable function - javascript

I have a bit of code that counts how many objects are in the returned json and gives me the total number.
loadCountSetOne = 0;
$.each(dataSetOne.user.customers, function(key, val) {
loads = Object.keys(val.loads).length;
loadCountSetOne = loadCountSetOne + loads;
console.log(loadCountSetOne);
});
This works fine, but since I'll need to count these a bunch of times I thought I'd move it into it's own function and call it when I need it with something like counter(val.loads);
count = 0;
function counter(itemToCount) {
result = Object.keys(itemToCount).length;
count = count + result;
console.log(itemToCount, result, count);
return count;
}
When I call the function the 1st time I get the right result. When I call it again it adds the 2nd result to the 1st and so on.
My understanding is that that is what it's supposed to do, but not what I need it to do. I tried resetting the value for count is various places but it didn't work.
Is there a way to make this function give me a result based on the number of objects in itemToCount each time it's called?
Thanks.

You can't do this in the counter() function itself, since it has no way of distinguishing the first call (which should reset the variable) with subsequent calls. You could pass the array index, and it could reset the total when the index is 0, but this is not a good general solution because you might want to use the function in other ways.
You just need to reset the variable before the $.each() loop:
count = 0;
$.each(dataSetOne.user.customers, function(key, val) {
counter(val.loads);
});
Or you could use reduce, which is designed for accumulating values.
function counter(total, itemToCount) {
var result = Object.keys(itemToCount).length;
total += result;
console.log(itemToCount, result, total);
return total;
}
var count = dataSetOne.user.customers.reduce(function(total, current) {
return counter(total, current.loads);
}, 0);

Related

How to create a function to count items from a set and store counts in an array parallel to one containing related items?

I am having trouble completing one of the last assignments in my semester-long high school-level programming class. I have been assigned to create a JavaScript program which counts the amount of time different ZIP codes appear in a set and output parallel arrays containing the zip codes and their counts. I am having difficulty getting the values to output. I believe that the respective zips and counts aren't being entered into their arrays at all.
I'm not looking for an original solution to the problem. I'd just like someone to tell me why my code isn't working, and possibly what I can change in my code specifically to fix it.
Usually I would never ask for help like this. I actually took the class last semester and now that I'm at the end of the year I have the option of completing it to earn college credit. I have never been the best at working with functions, and that remains true now. In the code below are all the moving parts I'm allowed to work with. I know it looks messy and rudimentary, but it's all I know. I'd appreciate it if any answers use only the sorts of things I used in my code. Another note, I am required to use functions for 'all identifiable processes', but I'm pretty sure my instructor only cares about the final product, so I'm not sure that the functions really matter, even if they could help.
var records = openZipCodeStudyRecordSet(),
uniqueZips = [],
zipCounts = [],
output = "";
function project62Part1() {
table = document.getElementById("outputTable");
function countZips(zip) {
var currentZip,
count;
while (records.readNextRecord()) {
currentZip = records.getSampleZipCode();
if (zip === currentZip) {
count++;
}
}
return count;
}
function processZip(zip) {
var currentZip;
while (records.readNextRecord()) {
currentZip = records.getSampleZipCode();
for (i = 0; i < uniqueZips.length; i++) {
if (uniqueZips[i] === "") {
uniqueZips.push(currentZip);
zipCounts[i] = countZips(currentZip);
break;
}
if (zip !== uniqueZip[i]) {
uniqueZips.push(currentZip);
zipCounts[i] = countZips(currentZip);
}
}
}
}
function createOutput(string) {
for (i = 0; i < uniqueZips.length; i++) {
string += "<tr><td>" + uniqueZips[i] + "</td><td>" + zipCounts[i] +
"</td></tr>";
}
return string;
}
processZip();
output = createOutput(output);
table.innerHTML = "<tr><td>Zip Code</td><td>Count</td></tr>" + output;
}
The output is supposed to be additional rows of zips and counts added to a table that is already set up on the page. There are no important technical errors in the code.
This is to be accomplished through the function processZip, which is meant to add respective zip and count into table rows. However, it appears as though the zip and count arrays its getting info from haven't had anything put into them by the other functions. I don't know if it is because of error in calling the functions, or what's in the functions themselves.
The HTML page this is connected to calls the function project62Part1().
That code is kind of all over the place but here's the logic you ideally want to follow:
Loop over each record in your table (outer loop) to get the zip code.
Declare an 'isFound' variable and set it to false
For each iteration of the outer loop, loop over your entire array of zip codes (inner loop).
3a. If you get a match, set isFound to true, increment your zipcode counter += 1 on the same index (since they're parallel arrays)
3b. If, at the end of your inner loop, isFound is still false, add the zipcode to your array of zip codes, and add a new array element to your zip code counters setting it to 1.
Since your zip code array and your zip code counter are parallel arrays to each other, when isFound is false, you are creating entries in both arrays, keeping them parallel to each other.
If, on 3a isFound is true, you are on the index of the zip code array that the zip code belongs to, so it should be the same index for your counter array.
In your current process zip function, the first condition will never be true, because starting out, your array size is 0 and after you start populating that array, you will never have an empty string (unless, of course, the zip code itself was an empty string)
The second if statement you have that checks if zip !== uniqueZip[i] - you are only checking that current value of uniqueZips and ignoring every other value in the array, so you will almost always have the second condition as true
I've been playing with the newer JavaScript language and syntax and your item was a good candidate for me to try out.
I did approach the code a little differently such as making the use of a Set for the unique values. Saves on code by not having to check and see if the value exists because the Set will never allow duplicate values in.
var uniqueZips = new Set();
const zipcodes = [21060, 22422, 25541, 43211, 21060, 22422, 22422, 43211, 43211, 43211];
function project62Part1() {
function processZipCodes() {
for(let index in zipcodes){
// We add every value because a SET will only allow you to add it once.
uniqueZips.add(zipcodes[index]);
}
}
// Structure our zipcode data information
function organizeZipCodeData() {
let response = {data:[]};
uniqueZips.forEach(function(zip) {
response.data.push( { 'zipcode':zip, 'appears': countZipAppearances(zip) })
});
return response;
}
function countZipAppearances(zip) {
// Default to zero even though you never expect an undefined
let count = 0;
zipcodes.forEach(function(zval) {
if (zip === zval) {
count++;
}
});
return count;
}
function showZipcodeInformation(data){
for (var index in data) {
if (data.hasOwnProperty(index)) {
var entry = [data[index]][0];
console.log(entry.zipcode, entry.appears);
}
}
}
// UI CONTENT: Construct the UI view from the data
function generateHtmlView(data){
let htmlview = "<table><tr><td>Zip Code</td><td>Count</td></tr>";
for (var index in data) {
if (data.hasOwnProperty(index)) {
var entry = [data[index]][0];
htmlview+="<tr><td>"+entry.zipcode+"</td><td>"+entry.appears+"</td></tr>";
}
}
htmlview += "</table>";
console.log(htmlview);
return htmlview;
}
// //////////////////////////////////////////////////////
// Call to gather the zipcodes
processZipCodes();
// Call to organize the zipcode data
let output = organizeZipCodeData();
// See what we have in the organized data
showZipcodeInformation(output.data);
// See what we have in the html content
generateHtmlView(output.data);
}
// Initiate the process
project62Part1();

Use closure to sum numbers

Currently I have a closure in JS that looks like the following:
var addTo = function(num){
var add = function(inner){
return inner + num;
};
return add;
};
var sum = new addTo(1);
My goal is to use the above closure to compute the sum from 1 all the way to 100 (i.e. sum = 1+2+3+...+99+100). Any help? I know a loop is needed, but am unsure of what should go inside the loop and how to use closure to achieve the goal. Thanks guys.
Currently I have a closure in JS that looks like the following:
All functions create closures, they're only remarkable when advantage is taken of them. ;-)
var addTo = function(num){
I don't know why function expressions are used when declarations are clearer (to me):
function addTo(num) {
then there's:
var add = function(inner){
return inner + num;
}
return add;
}
Which (sticking with an expression) can be:
return function (inner) {return inner + num};
}
Then you call it with new:
var sum = new addTo(1);
which causes addTo to create a new object that is not used, so you might as well do:
var sum = addTo(1);
which produces exactly the same result. So:
function addTo(num) {
return function (inner) {return inner + num};
}
var sum = addTo(1);
document.write(sum(3));
However, this is really just a version of Currying, so that sum will just add the supplied value to whatever was initially supplied to addTo.
If you want to add all the numbers from 0 to some limit, you just need a loop, no closure required:
function sumTo(num) {
var total = 0;
for (var i = 0; i <= num; i++) {
total += i;
}
return total;
}
document.write(sumTo(5)); // 15
Note that supplying a negative number will result in an endless loop, you should protect against that (I'll leave it up to you to work out how).
Try
function sum(x) {
var input = x;
function add(y) {
return input + y;
}
return add;
}
//var sum1 = sum(2);
//console.log(sum1(3)); // 5
console.log(sum(2)(3)); //5
Maybe you want to use recursive instead of loops?
function addTo(initial) {
function add(adder) {
if (initial < 100) {
initial+=1
return add(adder+initial)
}
else {
return adder
}
}
return add(initial)
}
document.write(addTo(1))
As long as the initial values don't go over 100, it would just add with sum of all calculation before + itself + 1.
It looks like the addTo() function returns another function into sum that will add whatever you pass it to the original number (or I assume that's what you meant to write; the first thing to do is change the statement inside of add() to use a += instead of just + to make sure you save the result).
Since you want to add each number from 2 to 100 (since you already passed 1 into addTo()), try writing a for loop that runs from 2 to 100 passing each one into the sum() function to add them all together. Here's an example:
var sum = addTo(1);
for (var i=2; i<100; i++) sum(i);
var result = sum(100);
Here I added 100 after the loop since I wanted to grab the final result. You could also add 100 in the loop and use sum(0) to get the result without changing it after the loop.

Why does resetting an array after looping through it only work for two complete iterations?

I have a fairly straight forward question. I want to loop through one array, making sure not to select the same value twice, (for which I used splice to move the selected values to a new array) and then, when all values have been selected, start the process over (for which I used slice to make a copy, and then clear out the old one. However, for some reason, be it async or what, my technique only works for two loop completions (then the array does not flip). I did try using callbacks but had the same (lack of) success.
Can anyone suggest any reason why this is happening? my code is below, and you can see from the picture that it works correctly for two iterations.
var oldArray = [4441, 2444, 2343, 64]; //Beginning array
var oldestArray = []; //"Holder" array
function getItem() {
var item;
var index;
if(!oldArray.length) {
//Check to see if all values have been selected, then switch arrays
oldArray = oldestArray.slice();
oldestArray = [];
console.log('The arrays have been reset');
}
index = Math.floor(Math.random() * (oldArray.length));
item = oldArray.splice(index, 1);
oldestArray.push(item[0]);
return item;
}
var fun = function() {
console.log('This is old Array');
console.log(oldArray);
console.log('This is oldest Array');
console.log(oldestArray);
}
for(var i = 0; i < 25; i++) {
getItem();
fun();
}
I can't really figure out what the Firebug console is doing, but this seems to be an issue with console.log()ging a variable that is changing after it's logged. You can see the expected output if you coerce the arrays into strings when you output them:
console.log('This is old Array');
console.log("[" + oldArray + "]");
console.log('This is oldest Array');
console.log("[" + oldestArray + "]");
http://jsfiddle.net/W7c9C/1/

Higher Order Functions - Eloquent JS

I have been reading through Chapter 5 last night and throughout the morning and can't seem to get the higher order functions concepts to stick. Here are the examples:
//I understand this first function, I am including it because it is used in the next function.
function forEach(array, action) {
for (vari = 0; i < array.length; i++)
action(array[i]);
}
forEach(["Wampeter", "Foma", "Granfalloon"], print);
function sum(numbers) {
var total = 0;
forEach(numbers, function(number) {
total += number;
});
return total;
}
To my understanding the function sum is taking the argument numbers, which I believe comes in as an array? Now, when the forEach function is called (within sum), it takes the array numbers passed to sum and then it also takes an anonymous function?
I am really confused on what this anonymous function is actually doing. It is taking the parameter number but what else is it doing? Does this anonymous function imply that in that parameter, a function like print or show will be passed the parameter number? In other words it would look something like this
function([10,12,11]) {
var total = 0
forEach([10,12,11]), show(???)
//at this point it would iterate over the array, and use the action passed to display `//the pointer in the array. What I think is happening is that it is taking this pointer value and adding it to the total.` //
I have been trying to wrap my head around this example for a while, if anyone knows of a good explanation or any other documentation to read over I would greatly appreciate it, thanks!
The anonymous function is applied to every currently selected element. You can see better how this works if you unroll (execute stepwise) the loop (pseudocode, * means current element):
var total = 0;
forEach([*1, 2, 3]), fun(1)) => total = 0 + 1 = 1
forEach([1, *2, 3]), fun(2)) => total = 1 + 2 = 3
forEach([1, 2, *3]), fun(3)) => total = 3 + 3 = 6
You can rewrite the sum function like this:
// because there is no "pass by reference" in JavaScript for
// "simple" types, total must be wrapped in an object
// in order to return the sum through the parameter for the showcase
var result = { total: 0 }
function sum(numbers_array) {
for (var i = 0; i < numbers_array.length; i++) {
accumulate(result, numbers_array[i]);
}
}
function accumulate(acc, number) {
acc.total += number;
}
In this case the accumulate function does the same as the anonymous function. When the accumulate function is declared within the scope of the sum function, then the total variable is like global (it is known) to the accumulate function and then there is no need of the first parameter, so the function becomes like the one you already know:
var total = 0;
function sum(numbers_array) {
function accumulate(number) {
total += number;
}
for (var i = 0; i < numbers_array.length; i++) {
accumulate(numbers_array[i]);
}
}
Next step would be to extract and pass the accumulate function as parameter:
var total = 0;
function accumulate(number) {
total += number;
}
// notice, that JavaScript knows how many parameters your function expects
function sum(numbers_array, action) {
for (var i = 0; i < numbers_array.length; i++) {
action(numbers_array[i]);
}
}
What left is to extract the iteration and the code will look like this one in the book.
Let me see if I can explain this easily for you:
The forEach() function accepts two parameters, the first one called array is obviously an array or an array-like object, the second parameter called action is actually a function.
forEach() visits each element in the array passed to it and applies to each element in the array the function passed to it as the second parameter.
So forEach() calls the function passed to it named action for each element in the array and it gives the function the array element as a parameter.
The function sum(numbers) accepts an array as you have though, and it uses forEach() inside itself to calculate the sum of numbers in that array using the anonymous function.
Remeber that the anonymous function is called once for each element in the array passed to sum() so it actually sums the elements in the array.
In simple words : to make your code more generic and concise.
Ex:
Lets say we want to find the max element in an Array :
That's pretty easy and cool :
In java script we will write :
var array = [10,20,30,40,50,60]
function maxEle(array){
var max = array[0];
for(var i=0;i< array.length;i++){
if(max < array[i]){
max = array[i];
}
}
console.log(max);
}
So this will give me the maximum element in an array.
Now after few days, some one asked me that your max is working pretty cool, I want a function which will print the minimum in an array.
Again I will redo the same thing, which i was doing in finding Max.
function minEle(array){
var min = array[0];
for(var i=0;i< array.length;i++){
if(min > array[i]){
min = array[i];
}
}
console.log(min);
}
Now this is also working pretty cool.
After sometime, another requirement comes up : I want a function which will print the sum of all the elements of the array.
Again the code will be similar to what we have written till now, except now it will perform summation.
function sumArr(array){
var sum = 0;
for(var i=0;i< array.length;i++){
sum = sum + array[i];
}
}
console.log(sum);
}
Observation :
After writing these bunch of codes, I m rewriting almost the same thing in every function, iterating over the Array and then performing some action.
Now writing the repetitive code is not a cool stuff.
Therefore we will try to encapsulate the varying part i.e action viz min, max, summation.
Since its feasible to pass functions as arguments to a function in FPL.
therefore we will re-factor our previously written code and now write a more generic function.
var taskOnArr = function(array, task){
for(var i=0;i<array.length;i++){
task(array[i]);
}
}
Now this will be our generic function, which can perform task on each element of Array.
Now our tasks will be :
var maxEle = array[0];
var taskMaxEle = function(ele){
if(maxEle < ele){
maxEle = ele;
}
}
Similarly for min element :
var minEle = array[0];
var taskMinEle = function(ele){
if(minEle > ele){
minEle = ele;
}
}
Also for summation of Array :
var sum = 0;
var taskSumArr = function(ele){
sum = sum + ele;
}
Now we need to pass functions to taskOnArr function :
taskOnArr(array,taskSumArr);
console.log(sum);
taskOnArr(array,taskMinEle);
console.log(minEle);
taskOnArr(array,taskMaxEle);
console.log(maxEle);

Track which JSON objects have been output

I'm outputting 20 or so JSON objects randomly by setting the index to a randomNumber() initially when the page is loaded.
I'm refreshing each JSON object individually that has already been output on a timeInterval.
To keep track of which JSON items have been output I am storing the index of each item into an array via arrUsed.push[index]
Now trying to write the function that will update() each JSON objects individually and am currently stuck on how I can update the each div with the information from a new JSON object that has not already been output (pushed to the arrUsed[]).
Here's the function I have so far:
function reloadItems() {
var itemTotal = $('div.item').length; // Total number of items loaded initially
var randomNumber=Math.floor(Math.random()*301) //returns number
index = randomNumber; // Sets index to be used in JSON data to random number
}
The array that contains the already output index's is declared globally: arrUsed = []; Each item that is output initially when the page load is being stored to the array fine, so that part is covered. It's a matter of choosing a random JSON object, checking to ensure it is not in the array/not been output already, and then updating the div on the page.
Here's the previous question that has led me to this point
Here's a working example of a JSON/AJAX Ticker:
http://ticker.weisser.co/
Per twhyler's specification, it randomly swaps an item every 4.5 seconds, keeping track of ones that have already been seen. Once they've all been seen, it starts over again:
Code Files:
default.html (Main Program)
template.html (Product Template)
UK.top.20.html (JSON Data)
ticker.js (jQuery)
ticker.css (Style Sheet)
First, we should store template.html in our global template variable and fire the getJson() function:
template = '';
....
$(document).ready(function () {
$.get('template.html', function(html) { template = html; getJson(); });
});
Then, we'll store the JSON into our data variable and fire the initialize() function:
data = ''; // JSON data will be stored here
myurl = 'UK.top.20.html';
....
function getJson() {
$.getJSON(myurl, function (JSON) { data = JSON; initialize(); });
}
Here, we'll call the load() function 3 times to populate our 3 product div's with data right away. Then we set i back to 2 (that's so it will change the 3rd DIV first), and schedule tick() to fire in 4.5 seconds:
tick_time = 4500;
....
function initialize() { // Start with the first 3
for (i = 1; i < 4; i++) { load(); }
i = 2;
setTimeout('tick()', tick_time);
}
Before we explain the load() function, let's talk about `String.prototype.f' at the bottom of the script:
String.prototype.f = function () { var args = arguments; return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; }); };
This function works similar to String.Format(formatstring, arg1, arg2, arg3...) in C# or sprintf($formatstring, arg1, arg2, arg3...) in PHP. Here's an example:
formatstring = 'Roses are {0}, Violets are {1}, String.Format {2} and so do {3}!';
result = formatstring.f('red', 'blue', 'rocks', 'you');
alert(result);
So as you can see, this String.prototype.f function comes in very handy!
The first thing the load() function will do is set rid = randomId();. Let's take a look at the randomId() function next. The first thing it does is get a number from 1-20 (based on the length of our JSON data). Then, it checks to see if our seen array is the same size as our JSON data, and if it is - it sets it back to 0. Next it makes sure that our rid hasn't been seen recently, and if it has, the function recursively calls itself again till it gets a unique random id. Otherwise, we store the rid in our seen array and return the value.
function randomId() {
rid = Math.floor(Math.random() * data.results.length);
if (seen.length == data.results.length) { seen.length = 0; }
if ($.inArray(rid, seen) == -1) {
seen.push(rid);
return rid;
} else { return randomId(); }
}
Next in our load() function after getting that random ID, we setup item and plat as convenient shortcuts. plat_html is a temporary storage place for the Platform elements. The for-loop looks at all the Platform data in our JSON and uses our String.Format function to fill our plat_html string.
Finally, we allow the current value of i (which is global) determine which #product_ gets updated, and template.f fills our template with JSON data which is done with a smooth jQuery animation thanks to .fadeIn().
function load() {
rid = randomId();
item = data.results[rid];
plat = item.Platform;
plat_html = '';
for (var j = 0; j < plat.length; j++) {
plat_html += plat_fmt.f(
plat[j].Hardware,
plat[j].Market
);
}
$('#product_' + i).html(
template.f(
item.Image,
item.id,
item.AgeRating,
item.WikiUrl,
item.Title,
item.Source,
item.Genre,
plat_html
)
).fadeIn();
}
Lastly, let's take a look at the recursive tick() function. It begins by incrementing our global i variable and setting it back to 1 when it reaches 4. Then we perform an animated fadeOut() on our #product_ element and wait till it's finished till we call load() again. Then, it schedules itself to run again in another 4.5 seconds.
function tick() {
i++; if (i == 4) { i = 1; }
$('#product_' + i).fadeOut(function() { load(); });
setTimeout('tick()', tick_time);
}
That's it!
Use $.inArray(): http://api.jquery.com/jQuery.inArray/
$.inArray(indexInQuestion, arrUsed);
It will return -1 if the element is not in the array, so you will know wether indexInQuestion was already added to arrUsed.

Categories