I have the following function, which is called when a google forms is submitted. I'm trying to concatenate all answers into a single array that's gonna be used latter:
function onFormSubmit(e) {
var respostas = e.namedValues;
for(item in respostas){
rp = rp.concat(respostas[item]);
}
}
But I would like to drop the timestamp that comes together with the answers. I can access it with respostas['Timestamp'], but I can't find a way to drop or ignore it. The documentation didn't help much.
var cp = [];
function onSubmitForm(e) {
var respostas = e.namedValues;
for (var name in respostas) {
if (respostas.hasOwnProperty(name) {
if (name !== 'Timestamp') {
cp.push(respostash[name]);
}
}
}
}
This is what I would suggest. Using concat to add an item is overkill, you can just push it. Also is a good practice when you are looping over object properties to make sure that they are its own properties of that object, not inherited from prototype. You can read more about it here
You can check the name of the property before concatenate it with the rest.
If the key item equals Timestamp (the undesired property) just skip the current loop.
for(item in respostas) {
if (item === 'Timestamp') {
continue;
}
rp = rp.concat(respostas[item]);
}
EDIT: Based on comments, OP attests that item in the for..in loop is a integer, but, unless his/her code differs radically from the docs, the variable should hold strings, not numbers.
var respostas = {
'First Name': ['Jane'],
'Timestamp': ['6/7/2015 20:54:13'],
'Last Name': ['Doe']
};
for(item in respostas) {
console.log(item);
}
e.namedValues returns a JSON Object with custom keys.
var jsonObj = e.namesValues;
/* e.namedValues returns data like this...
{
"test1": "testval1",
"test2": "testval2",
"test3": "testval3",
}
*/
for(item in respostas){
Logger.log(item); //Key
Logger.log(respostas[item]); //Value
}
This should let you access the key or value on the items in respostas.
The accepted answer is better as it does more to help the user to fix their exact problem, however, I will leave this here for future users in case they want to understand how to access the variables in the object that Google Apps Scripts returns.
Related
The Problem is the following:
I have a JSON file that has objects with the following name: "item0": { ... }, "item1": { ... }, "item2": { ... }. But I can't access them when going through an if method.
What I've done so far:
$.getJSON('/assets/storage/items.json', function(data) {
jsonStringify = JSON.stringify(data);
jsonFile = JSON.parse(jsonStringify);
addItems();
});
var addItems = function() {
/* var declarations */
for (var i = 0; i < Object.keys(jsonFile).length; i++) {
path = 'jsonFile.item' + i;
name = path.name;
console.log(path.name);
console.log(path.type);
}
}
If I console.log path.name it returns undefined. But if I enter jsonFile.item0.name it returns the value. So how can I use the string path so that it's treated like an object, or is there an other way on how to name the json items.
As others stated 'jsonFile.item' + i is not retrieving anything from jsonFile: it is just a string.
Other issues:
It makes no sense to first stringify the data and then parse it again. That is moving back and forth to end up where you already were: data is the object you want to work with
Don't name your data jsonFile. It is an object, not JSON. JSON is text. But because of the above remark, you don't need this variable
Declare your variables with var, let or const, and avoid global variables.
Use the promise-like syntax ($.getJSON( ).then)
Iterate object properties without assuming they are called item0, item1,...
Suggested code:
$.getJSON('/assets/storage/items.json').then(function(data) {
for (const path in data) {
console.log(data[path].name, data[path].type);
}
});
What you want is to use object notation using a dynamic string value as a key instead of an object key. So, instead of using something like object.dynamicName you either have use object[dynamicName].
So in your example it would be like this.
path = 'item' + i;
jsonFile[path].name
I'm afraid you cannot expect a string to behave like an object.
What you can do is this:
path = `item${i}`
name = jsonFile[path].name
I have a dataset of records that look like this :
[{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":3
},
{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":1
}
]
The ct value changes in every record. If d1d, d1h, and d15m are the same in one or more records, I need to combine those records into one with the sum of all the ct values.
I do have jquery, can I use grep for this?
I realize the server side could do a better job of getting me this data , but I have zero control over that.
You don't have to use jQuery for this, vanilla JavaScript will do.
I'll show you two solutions to your problem;
Example 1: Abusing Array#reduce as an iterator
var intermediaryArray = [];
dataset.reduce(function(prev, curr) {
if(prev.d1d === curr.d1d && prev.d1h === curr.d1h && prev.d15m === curr.d15m) {
intermediaryArray.push({
d1d: prev.d1d,
d1h: prev.d1h,
d15m: prev.d15m,
ct: prev.ct + curr.ct
});
} else {
// push the one that wasn't the same
intermediaryArray.push(curr);
}
// return current element so reduce has something to work on
// for the next iteration.
return curr;
});
Example 2: Using Array#Map and Array#Reduce in conjunction
This example utilises underscore.js to demonstrate the logic behind what you want to do.
.map() produces the new array of grouped objects.
.groupBy() produces an array of subarrays containing the objects that pass the predicate that all objects must share the same d1d or grouping function.
.reduce() boils all subarrays down to one value, your object with both cts added to each other.
var merged = _.map(_.groupBy(a, 'd1d'), function(subGroup) {
return subGroup.reduce(function(prev, curr) {
return {
d1d: prev.d1d,
d1h: prev.d1h,
d15m: prev.d15m,
ct: prev.ct + curr.ct
};
});
});
Here's one possible solution:
var dataset = [{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":3
},
{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":1
}
]
function addCt(dataset) {
var ctMap = {}
var d1d, d1h, d15m, ct, key, value
for (var ii=0, record; record=dataset[ii]; ii++) {
key = record.d1d+"|"+record.d1h+"|"+record.d15m
value = ctMap[key]
if (!value) {
value = 0
}
value += record.ct
ctMap[key] = value
}
return ctMap
}
ctMap = addCt(dataset)
console.log(ctMap)
// { "2015-05-28T00:00:00.000Z|0|0": 4 }
You may want to construct the key in a different way. You may want set the value to an object containing the d1d, d1h, d15m and cumulated ct values, with a single object for all matching d1d, d1h and d15m values.
I'm creating a very simplified version of a drag and drop shopping cart with jqueryui.
My issue is regarding adding data(id, name, price) to an array.
I tried several methodes of adding the data (also an array) to the main container(array). But I keep getting this error: Uncaught TypeError: undefined is not a function
var data = [];
function addproduct(id,name,price){
//var d = [id,name,price];
data[id]["name"] = name;
data[id]["price"] = price;
data[id]["count"] = data[id]["count"]+1;
console.log(data);
}
the addproduct() function can be called by pressing a button
It is not entirely clear to me what type of data structure you want to end up with after you've added a number of items to the cart. So, this answer is a guess based on what it looks like you're trying to do in your question, but if you show a Javascript literal for what you want the actual structure to look like after there are several items in the cart, we can be sure we make the best recommendation.
You have to initialize a javascript object or array before you can use it. The usual way to do that is to check if it exists and if it does not, then initialize it before assigning to it. And, since you're keeping a count, you also will want to initialize the count.
var data = [];
function addproduct(id,name,price){
if (!data[id]) {
// initialize object and count
data[id] = {count: 0};
}
data[id]["name"] = name;
data[id]["price"] = price;
++data[id]["count"];
console.log(data);
}
And FYI, arrays are used for numeric indexes. If you're using property names like "name" and "price" to access properties, you should use an object instead of an array.
And, I would suggest that you use the dot syntax for known property strings:
var data = [];
function addproduct(id,name,price){
if (!data[id]) {
// initialize object and count
data[id] = {count: 0};
}
data[id].name = name;
data[id].price = price;
++data[id].count;
console.log(data);
}
It looks like what you want is an array of objects, although I would need a more detailed description of your problem to be clear.
var data = []
function addproduct(id, name, price)
{
data.push({'id': id, 'name':name, 'price': price, 'count': ++count});
console.log(data);
}
In localstorage I have key 'results' with this values:
[{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}]
To get the last item I use this:
// this is how I parse the arrays
var result = JSON.parse(localStorage.getItem("result"));
for(var i=0;i<result.length;i++) {
var item = result[i];
$('element').val(item.href);
}
How can I get the href for item-3 or for a specific ID?
Using native Array.filter
If you are targeting only modern browsers (IE9+ or a recent version of any other major browser) you can use the JavaScript 1.6 array method filter.
var testItem,
data = [{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}];
function getItemById(data, id) {
// filter array down to only the item that has the id
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
var ret = data.filter(function (item) {
return item.id === id;
});
// Return the first item from the filtered array
// returns undefined if item was not found
return ret[0];
}
testItem = getItemById(data, 'item-3');
Working example
Manually looping over the data
If you can't use filter you are probably stuck with just using a loop:
var testItem,
data = [{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}];
function getItemById(data, id) {
var i, len;
for (i = 0, len = data.length; i < len; i += 1) {
if(id === data[i].id) {
return data[i];
}
}
return undefined;
}
testItem = getItemById(data, 'item-3');
Working example
Even though brute-forcing it with a loop might seem less elegant than using Array.filter, it turns out that in most cases the loop is faster than Array.filter.
Refactoring to an object instead of an array
The best solution, assuming that the id of each of your items is unique, would be refactoring the way you are storing the data. Instead of an array of objects, use an object that uses the id as a key to store an object containing the href and icon key/property values.
var data = {
"item-1": {"href": "google.com", "icon": "google.com"},
"item-2": {"href": "youtube.com", "icon": "youtube.com"},
"item-3": {"href": "google.com", "icon": "google.com"},
"item-4": {"href": "google.com", "icon": "google.com"},
"item-5": {"href": "youtube.com", "icon": "youtube.com"},
"item-6": {"href": "asos.com", "icon": "asos.com"},
"item-7": {"href": "google.com", "icon": "google.com"},
"item-8": {"href": "mcdonalds.com", "icon": "mcdonalds.com"}
};
This would make accessing items even easier and faster:
var data = JSON.parse(localStorage.getItem("result"));
data["item-3"].href;
jQuery has filter helper for that:
$(result).filter(function(){return this.id == "item-3";})[0]
Function for href of item with specific id would be:
function getItemHrefById(json, itemId){
return json.filter(function(testItem){return testItem.id == itemId;})[0].href;
}
And sample usage is:
var href = getItemHrefById(result, "item-3");
You can see working example on http://jsfiddle.net/LXvLB/
UPDATE
If you cannot read item from local storage, maybe you forgot to call JSON.stringify when setting value:
localStorage["results"] = JSON.stringify([{"id":"item-1","href":"google.com","icon":"google.com"},
{"id":"item-2","href":"youtube.com","icon":"youtube.com"},
{"id":"item-3","href":"google.com","icon":"google.com"},
{"id":"item-4","href":"google.com","icon":"google.com"},
{"id":"item-5","href":"youtube.com","icon":"youtube.com"},
{"id":"item-6","href":"asos.com","icon":"asos.com"},
{"id":"item-7","href":"google.com","icon":"google.com"},
{"id":"item-8","href":"mcdonalds.com","icon":"mcdonalds.com"}])
You need to convert json to string to be properly serialized (and to use JSON.parse to get JSON back)
This is final example.
EDIT
As Useless Code pointed out, this metod is substantially slower than native filter function (and custom loop but I think that introducing few new lines of code to save 20-30ms is overkill unless performance is a issue), so I'm updating my example to not use jquery filter. +1 please for his answer for that.
Also, what is important to point out here, if this array would have hundreds instead of 8 bookmarks, for loop would probably be statistically about twice faster (as it does not have to iterate through rest of the array). But, in that case it would probably be a good idea to put for loop into function which returns first found item which satisfies condition, and with prototypejs it probably can even be hooked up to array.
for the jquery filter method, I think using a callback function, and bind the search parameter is more elegant and readable:
function filterById(id, i, obj) {
return obj.id === id;
}
function getItemHrefById(json, itemId) {
return $(json).filter(filterById.bind(null, itemId))[0].href;
}
da usual fiddle
(however, i prefer the "for loop" approach" for this!!)
I have an array like this (with just over 3000 objects instead of the 3 here):
items = [{name:'charlie', age:'16'}, {name:'ben', age:'18'}, {name:'steve', age:'18'}]
What's the best way to return an array with just the objects of people who are 18? So I want:
items = [{name:'ben', age:'18'}, {name:'steve', age:'18'}]
The best I can think of is this (using jQuery):
newArray = []
$.each(items, function(index, item) {
if(item.age=='18') {
newArray.push(item)
}
})
Considering that there's 3000 thousand objects, and also that I'll be doing that comparison up to fifty times in one go, that's a lot of looping. Is there a better way?
You can use pure javascript
var wanted = items.filter( function(item){return (item.age==18);} );
And if your browser does not support the 1.6 version of javascript you can find an implementation of the filter method at https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
Update
Speedwise there is a huge varying (had an error in the test) difference from a normal loop (depending on browser).. Have a look at this little test i made at http://jsperf.com/array-filter-vs-loop/3
Get matched item and items using find() and filter() method
If you want first matched single item, use find() method which returns single object.
If you want all matched , use filter() method which returns array of objects.
let items = [{name:'charlie', age:'16'},
{name:'ben', age:'18'},
{name:'steve', age:'18'}]
let all = items.filter(item=> item.age==='18')
console.log(all);
let single = items.find(item=> item.age==='18')
console.log(single);
If you're going to do the search often it may be best to keep a version of your data in a form that is quick to access.
I've used underscore.js (http://documentcloud.github.com/underscore/) to make it easy for myself, but this code here will create an object that holds your data indexed by the age field.
You end up with something that looks like this:
{
"16": [
{
"name": "charlie",
"age": "16"
}
],
"18": [
{
"name": "ben",
"age": "18"
},
{
"name": "steve",
"age": "18"
}
]
}
The code:
var itemsByAge = _(items).reduce(function(memo, item) {
memo[item.age] = memo[item.age] || [];
memo[item.age].push(item);
return memo;
}, {});
alert(JSON.stringify(itemsByAge["18"]));
No matter which method you choose (items.filter or any "query language" for json), a for loop is inevitable.
If performance is a concern, I would recommend you to use pure javascript instead of libraries like jQuery which will add overheads to the whole processing as is evident here.
Thus, your code would look like:
var newArray = [];
for(var i=0;i<items.length;i++) {
var item = items[i];
if(item.age == '18') {
newArray.push(item);
}
});
making use of javascript magnificent function eval() which evaluates string as code at runtime, we can define a prototype method for Array type
Array.prototype.where = function (query) {
var newArray = [];
for(var i=0; i<this.length; i++) {
var item = this[i];
if(eval( "item" + query )) {
newArray.push(item);
}
}
return newArray;
};
and use it with any array, passing the query as string
var newArray= items.where('.age >= 18');
Use the filter method of the array, it calls the provided callbackfunction once for each element in an array.
array.filter(<callbackfucntion>[, <Object to use>])
once i had such problem and i solved it like this
1- create an array of array
2- each index create an Index record
e.g.
var pAry=[];
var cAry=[{name:'ben', age:'18'}, {name:'steve', age:'18'}]
pAry[17]=cAry;
This way when u require person with age 18, you will get on index 17.