I have the following javascript object
[Object { url="http://domain.com/abc", qty="1" }, Object { url="http://myurl.com/cde", qty="2" }]
I want to be able to loop through the object and output the URL using console.log() based on the qty variable.
So in this instance domain.com/abc would display once & the myurl.com/cde would display twice as the qty is set to 2.
I have something like the following but needs some work..
cart.forEach(function(value) {
var qty = value.qty;
var url = value.url;
var i = 0;
while ( i < qty ) {
// logic needed here (i believe)
i++;
}
}
That's how one can implement String.repeat in JS:
var repeatedString = Array(repeatsCount + 1).join(stringToRepeat);
... so in your case it'll be just ...
console.log(Array(+value.qty + 1).join(value.url));
Unary plus is a shortcut for Number(value.qty): it looks like you got a string there.
But it looks you actually need to collect all the urls instead. That's one possible way to do that:
var arrayOfUrls = [];
cart.forEach(function(value) {
for (var i = value.qty; i--) {
arrayOfUrls.push(value.url);
}
});
Alternative (.reduce-based):
var arrayOfUrls = cart.reduce(function(arr, value) {
for (var i = value.qty; i--) {
arr.push(value.url);
}
return arr;
}, []);
Related
I've searched high and wide for an answer but can't seem to find it. I am trying to alter my custom function that looks up sitemap URL's and the date they were updated to accept a range of inputs.
Here is the current function that works:
function sitemap(sitemapUrl, namespace) {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement();
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9");
var urls = root.getChildren('url', sitemapNameSpace);
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc', sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod', sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
return array;
}
I've tried using Google's example below but doesn't seem to work however I incorporate it into my function. Any ideas?
function DOUBLE(input) {
if (input.map) { // Test whether input is an array.
return input.map(DOUBLE); // Recurse over array if so.
} else {
return input * 2;
}
}
Edit: This is how I tried to use Google's example for my function:
function sitemaps(sitemapUrl) {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement()
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9")
var urls = root.getChildren('url', sitemapNameSpace)
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc',sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod',sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
if (sitemapUrl.map) {
return sitemapUrl.map(sitemaps);
} else {
return array
}
You are no using the same format as the Google example. As of right now you are checking if the input is an array after actually retrieving the data.
But you using fetch with an array as input could trigger an Error and the function may no get to the point where it checks if the sitemapUrl can be used with map.
Also take into account that map will call the function in every single element of the array and return an array with a result for each of element. So in your case B3:B6 would call the function for the value at B3, B4, B5 and B6 and return an array of length 4 with the result. For your case in which you want a single list you need to flattern the array afterwards
I would change your function to be like this:
function sitemaps(sitemapUrl) {
if (sitemapUrl.map) {
return sitemapUrl.map(sitemaps).flat();
} else {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement()
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9")
var urls = root.getChildren('url', sitemapNameSpace)
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc', sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod', sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
return array
}
}
Although what you are doing is fine take into account that it also exists a way to retrieve all the request at the same time (
UrlFetchApp.fetch()) but for this specific case you would need to flatten a reshape the input array.
I am trying to create a reverse function to the String type in javascript. My code is like
String.prototype.reverse = function () {
var s = "";
for(var i=this.length;i>=0;i--){
s+=this[i];
}
return s;
}
When I try to use it on for example like "test".reverse();
instead of giving "tset" it's giving "undefinedtset"
I am setting the variable like var s = ""; inside the function, still undefined is coming. Why is it so?
You just need to change var i=this.length to var i=this.length-1, because an array starts at position 0 :
String.prototype.reverse = function () {
var s = "";
for(var i=this.length-1;i>=0;i--){
s+=this[i];
}
return s;
}
this.length gives you 4 (4 letters in test word) and you start iterating from 4 to 0.
The problem is that nothing exists under 4 index (your letters are stored in 0-3 positions).
Try with:
for (var i = this.length - 1; i >= 0; i--) {
s += this[i];
}
The reason why your code isn't working has been answered by the others already, but if you want a shorter version of the reverse, you can do it like this:
String.prototype.reverse = function(){
return this.split('').reverse().join('');
}
console.log('Hello World'.reverse())
Currently working on an application which requires to display a set of values in different currencies. This is part of an application but everything I provide here should be enough as this is the main section I am working with. I have a json file which is read in and it is stored into an array called valuesArray, this array has all the information, such as the amount, currency, etc. With the currencies being sorted with highest first to the lowest on display like this:
EUR 500.00
USD 200.00
This is the code that I have created but it seems like this wouldn't be effective the more currencies I have. I've just put an array declaration above the function but just above this function is where I do all the json stuff and adding it into the array. $scope.valuesArray has data at this point.
$scope.valuesArray =[];
$scope.total = function()
{
var eur_total = 0;
var usd_total = 0;
if (typeof $scope.valuesArray != 'undefined')
{
var length = $scope.valuesArray.length;
for (var i = 0; i<length ; i++)
{
switch($scope.valuesArray[i].currency)
{
case "USD":
usd_total += parseFloat($scope.valuesArray[i].value);
break;
default:
eur_total += parseFloat($scope.valuesArray[i].value);
break;
}
}
}
var cost_total= [usd_total,eur_total];
total.sort(function(a, b){return b-a});
return format_to_decimal(total[0]) + "\x0A" + format_to_decimal(total[1]);
}
In my for loop I go through every single data in the array and break each currency down within the switch statement and finding the total amount of each currencies.
The last bit is kind of temporary as I couldn't figure out a different way of how to do it. I sort the totals for the currencies I have from the highest at the top.
I return the function with a function call for format_numeric_with_commas which gives me the value in proper currency format and this displays the value. Will update this and add that code when I get to it. But I have used the indexes as a rough logic to show what I want to get out of it. So in this case, total[0] should be 500.00 and total[1] should be 200.00.
On top of this I want to be able to display the currency type for each. So like the example above.
You can try to save all the calculations in the array with currency index.
$scope.valuesArray = [];
$scope.total = function () {
var totalsArray = [];
if (typeof $scope.valuesArray != 'undefined') {
var length = $scope.valuesArray.length
for (var i = 0; i < length; i++) {
if (!totalsArray[$scope.valuesArray[i].currency]) {
totalsArray[$scope.valuesArray[i].currency] = 0;
}
totalsArray[$scope.valuesArray[i].currency] += $scope.valuesArray[i].value;
}
}
var cost_total = [];
for (var k in totalsArray) {
cost_total.push(currency:k,value:totalsArray[k]);
}
cost_total.sort(function (a, b) {
return b.value - a.value
});
return format_to_decimal(cost_total[0].value)+cost_total[0].currency + "\x0A" + format_to_decimal(cost_total[1].value);
I'm working on exercism question and am stuck on one of the jasmine-node based tests, which says that I should be able to generate 10000 random names without any clashes (e.g. 2 randomly generated names match). This is the test:
it('there can be lots of robots with different names each', function() {
var i,
numRobots = 10000,
usedNames = {};
for (i = 0; i < numRobots; i++) {
var newRobot = new Robot();
usedNames[newRobot.name] = true;
}
expect(Object.keys(usedNames).length).toEqual(numRobots);
});
What I think I need to do is:
Create an array to hold all the names (robotNames),
Each time a name is generated, check if it exists in the array,
If it does, generate another name,
If it doesn't, add it to the array.
And here is my code so far...
"use strict";
var robotNames = [];
var name;
var Robot = function() {
this.name = this.generateName();
};
Robot.prototype.generateName = function() {
var letters = "";
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var numbers = "";
var digits = "0123456789";
// generate random characters for robot name...
for( var i=0; i < 2; i++ ) {
letters += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
};
for( var i=0; i < 3; i++ ) {
numbers += digits.charAt(Math.floor(Math.random() * digits.length));
};
name = letters+numbers;
// Loop through array to check for duplicates
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
this.generateName();
return;
} else {
robotNames.push(name);
}
}
return name;
};
Robot.prototype.reset = function() {
this.name = this.generateName();
};
module.exports = Robot;
The test fails with an error message: "Expected 9924 to equal 10000."
The '9924' number is slightly different each time I run the test. I'm thinking this means the generateName function is eventually generating 2 matching random names. It seems as though my loop for checking duplicates is not being run and I'm not sure why.
I have tried a couple of different versions of the loop but with no success. So my questions is a) is my approach correct and there is something wrong with the syntax of my loop? or b) have I got the wrong idea about how to check for duplicates here?
Any pointers appreciated, thanks.
The problem is in this bit:
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
this.generateName();
return;
} else {
robotNames.push(name);
}
}
...you probably only want to push your name if NONE of the names fail to match. Here you're adding it to the list as soon as you find ONE that doesn't match. You want something more like:
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
return this.generateName();
}
}
robotNames.push(name);
(actually, combined with the fact that you weren't even returning the recursive call to this.generateName(), I'm not sure how your program could work...)
Find a library with an implementation for Sets. Collections.js is a good example.
One property of a set is that it doesn't have duplicates. So when you add a value to a set it will look for a duplicate and then add the value if no duplicate exists.
(forgive me if I use slightly incorrect language - feel free to constructively correct as needed)
There are a couple posts about getting data from JSON data of siblings in the returned object, but I'm having trouble applying that information to my situation:
I have a bunch of objects that are getting returned as JSON from a REST call and for each object with a node of a certain key:value I need to extract the numeric value of a sibling node of a specific key. For example:
For the following list of objects, I need to add up the numbers in "file_size" for each object with matching "desc" and return that to matching input values on the page.
{"ResultSet":{
Result":[
{
"file_size":"722694",
"desc":"description1",
"format":"GIF"
},
{
"file_size":"19754932",
"desc":"description1",
"format":"JPEG"
},
{
"file_size":"778174",
"desc":"description2",
"format":"GIF"
},
{
"file_size":"244569996",
"desc":"description1",
"format":"PNG"
},
{
"file_size":"466918",
"desc":"description2",
"format":"TIFF"
}
]
}}
You can use the following function:
function findSum(description, array) {
var i = 0;
var sum = 0;
for(i = 0; i < array.length; i++) {
if(array[i]["desc"] == description && array[i].hasOwnProperty("file_size")) {
sum += parseInt(array[i]["file_size"], 10);
}
}
alert(sum);
}
And call it like this:
findSum("description1", ResultSet.Result);
To display an alert with the summation of all "description1" file sizes.
A working JSFiddle is here: http://jsfiddle.net/Q9n2U/.
In response to your updates and comments, here is some new code that creates some divs with the summations for all descriptions. I took out the hasOwnProperty code because you changed your data set, but note that if you have objects in the data array without the file_size property, you must use hasOwnProperty to check for it. You should be able to adjust this for your jQuery .each fairly easily.
var data = {};
var array = ResultSet.Result;
var i = 0;
var currentDesc, currentSize;
var sizeDiv;
var sumItem;
//Sum the sizes for each description
for(i = 0; i < array.length; i++) {
currentDesc = array[i]["desc"];
currentSize = parseInt(array[i]["file_size"], 10);
data[currentDesc] =
typeof data[currentDesc] === "undefined"
? currentSize
: data[currentDesc] + currentSize;
}
//Print the summations to divs on the page
for(sumItem in data) {
if(data.hasOwnProperty(sumItem)) {
sizeDiv = document.createElement("div");
sizeDiv.innerHTML = sumItem + ": " + data[sumItem].toString();
document.body.appendChild(sizeDiv);
}
}
A working JSFiddle is here: http://jsfiddle.net/DxCLu/.
That's an array embedded in an object, so
data.ResultSet.Result[2].file_size
would give you 778174
var sum = {}, result = ResultSet.Result
// Initialize Sum Storage
for(var i = 0; i < result.length; i++) {
sum[result[i].desc] = 0;
}
// Sum the matching file size
for(var i = 0; i < result.length; i++) {
sum[result[i].desc] += parseInt(result[i]["file_size"]
}
After executing above code, you will have a JSON named sum like this
sum = {
"description1": 20477629,
"description2": 1246092
};
An iterate like below should do the job,
var result = data.ResultSet.Result;
var stat = {};
for (var i = 0; i < result.length; i++) {
if (stat.hasOwnProperty(result[i].cat_desc)) {
if (result[i].hasOwnProperty('file_size')) {
stat[result[i].cat_desc] += parseInt(result[i].file_size, 10);
}
} else {
stat[result[i].cat_desc] = parseInt(result[i].file_size, 10);
}
}
DEMO: http://jsfiddle.net/HtrLu/1/