I apologise if this has been asked before but I can't seem to find a solution from other posts on here.
I'm trying to build a json array in local storage (which is fine) but want to check if an entry already exists before adding new values.
The Json itself
[{"title":"title1","url":"somefile1.pdf","background":"bg1.png"},
{"title":"title2","url":"somefile2.pdf","background":"bg2.png"},
{"title":"title3","url":"somefile3.pdf","background":"bg3.png"}]
Now how would I query the array to ensure only unique entries are being added?
Heres the code to add to array with
var oldItems = JSON.parse(localStorage.getItem('itemsArray')) || [];
var newItem = {
'title': title,
'url': url,
'background': background
};
// Need to check the newItem is unique here //
oldItems.push(newItem);
localStorage.setItem('itemsArray', JSON.stringify(oldItems));
I thought I could use the jquery unique function instead before setting the localstorage object
var cleanedItems = $.unique(oldItems);
localStorage.setItem('itemsArray', JSON.stringify(cleanedItems));
but that didnt work...
You will have to loop over each of the items in the array that is parsed from local storage and perform an object equality test with the new item.
Object equality testing is not as simple as obj1 == obj2.
Here are some references to get you started
http://procbits.com/2012/01/19/comparing-two-javascript-objects
https://github.com/joyent/node/blob/e4cef1a0833e6d677298600e205a142d15639bf2/lib/assert.js#L205-L247
http://stamat.wordpress.com/2013/06/22/javascript-object-comparison/
http://underscorejs.org/docs/underscore.html#section-84
The following may end up working for you, by using JSON.stringify to compare the new object as a JSON string with the objects in the old array as JSON strings.
function objInArr(newObj, oldItems) {
var newObjJSON = JSON.stringify(newObj);
for (var i = 0, l = oldItems.length; i < l; i++) {
if (JSON.stringify(oldItems[i]) === newObjJSON) {
return true;
}
}
return false;
}
var oldItems = JSON.parse(localStorage.getItem('itemsArray')) || [];
var newItem = {
'title': title,
'url': url,
'background': background
};
// Need to check the newItem is unique here
if (!objInArr(newItem, oldItems)) {
oldItems.push(newItem);
}
localStorage.setItem('itemsArray', JSON.stringify(oldItems));
Related
I have two separate data array objects with multiple fields:
This is how the data object array looks like with the eventId field in it too.
The annotateData object has eventId field that is also present in the data object. I want to check which data element has the same eventId present in the annotateData and then merge that annotateData element to the data object element. So the output will have data object with annotateObject fields added to it.
data: [{
0:{ annotateData fields + already present data fields} //if eventId matches
}]
Is there a more efficient way to do so rather than running the loop through the entire data object?
I don't think that there is another efficient way of doing this rather than looping. Although I would implement a few helper methods to iterate over and merge (as given here):
Array.prototype.indexOfWithKeyValue = function(key, value) {
var index = -1;
var _this = this;
for (var i = 0; i < this.length; i++) {
var item = _this[i];
if (item[key] === value) {
index = i;
break;
}
}
return index;
};
Array.prototype.find = function(key, value) {
var index = this.indexOfWithKeyValue(key, value);
return this[index];
};
And then iterate over:
var annotateData = []; // sample data
var data = []; // sample data
angular.forEach(annotateData, function(aData) {
var matchingData = data.find("eventId", aData.eventId);
if (matchingData) {
// Matching fields from "annotateData" will be merged over "data"
angular.merge(matchingData, aData);
}
});
Im using the following code,
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
aSelectedDataSet.push(fnCreateEnt(aProp, oData, oPushedObject));
});
This is aSelectedDataSet values
and this is the values of OData
What I need is that before I do the push is to fill the listTypeGroup & listTypeGroupDescription (with the red arrow ) with values that Are inside the oData -> ListTypeGroupAssigment -> result (listTypeGroup & listTypeGroupDescription) , The index is relevant since I want to add just the value of the index in each iteration (since this code is called inside outer loop and the index determine the current step of the loop) ,How it can be done nicely?
The result contain 100 entries (always) and the a selected data will have 100 entries at the end...
Update :)
Just to be clear In the pic I show the values which is hardcoded for this run but the values can be any values, we just need to find the match between the both objects values...
I mean to find a match between to_ListTypeGroupAssigment in both object (which in this case exist ) and if in oData there is result bigger then one entry start with the matching ...
UPDATE2 - when I try Dave code the following happen for each entry,
This happen in the Jquery.extend line...any idea how to overcome this?
The following hard-coded of Dave:-) work perfect but I need generic code which doesnt refer to specific field name
jQuery.each(aDataSet, function(index, oData) {
oPushedObject = {};
fnCreatePushedEntry(aProperties, oData, oPushedObject);
var result = oData.to_ListTypeGroupAssignment.results[index];
oPushedObject.to_ListTypeGroupAssignment = {
ListTypeGroup: result.ListTypeGroup,
ListTypeGroupDescription: result.ListTypeGroupDescription
};
aSelectedDataSet.push(oPushedObject);
});
Im stuck :(any idea how to proceed here ?what can be wrong with the extend ?
should I use something else ? Im new to jQuery...:)
I think that this happen(in Dave answer) because the oData[key] is contain the results and not the specified key (the keyValue = to_ListTypeGroupAssignment ) which is correct but we need the value inside the object result per index...
var needValuesForMatch = {
ListTypeGroup: 'undefined',
ListTypeGroupDescription: 'undefined',
}
//Just to show that oPushedObject can contain additional values just for simulation
var temp = {
test: 1
};
//------------------This object to_ListTypeGroupAssigment should be filled (in generic way :) ------
var oPushedObject = {
temp: temp,
to_ListTypeGroupAssignment: needValuesForMatch
};
oPushedObject is one instance in aSelectedDataSet
and after the matching I need to do the follwing:
aSelectedDataSet.push(oPushedObject);
Is this what you're after:
OPTION ONE - DEEP CLONE FROM oData TO aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
var objectToClone = oData[childObject]['results'][index];
if(objectToClone)
$.extend(true,currentObject[childObject],objectToClone);
}
});
Here is your data in a fiddle with the function applied: https://jsfiddle.net/hyz0s5fe/
OPTION TWO - DEEP CLONE FROM oData ONLY WHERE PROPERTY EXISTS IN aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
if(typeof currentObject[childObject] !== 'object')
continue;
for(var grandChildObject in currentObject[childObject]) {
var objectToClone = oData[childObject]['results'][index][grandChildObject];
if(typeof objectToClone === 'object') {
$.extend(true,currentObject[childObject][grandChildObject],objectToClone);
} else {
currentObject[childObject][grandChildObject] = objectToClone;
}
}
}
Fiddle for option 2: https://jsfiddle.net/4rh6tt25/
If I am understanding you correctly this should just be a small change:
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
fnCreateEnt(aProp, oData, oPushObj);
//get all the properties of oData and clone into matching properties of oPushObj
Object.getOwnPropertyNames(oData).forEach(function(key) {
if (oPushObj.hasOwnProperty(key)) {
//oPushObj has a matching property, start creating destination object
oPushObj[key] = {};
var source = oData[key];
var destination = oPushObj[key];
//can safely assume we are copying an object. iterate through source properties
Object.getOwnPropertyNames(source).forEach(function(sourceKey) {
var sourceItem = source[sourceKey];
//handle property differently for arrays
if (Array.isArray(sourceItem)) {
//just copy the array item from the appropriate index
destination[sourceKey] = sourceItem.slice(index, index + 1);
} else {
//use jQuery to make a full clone of sourceItem
destination[sourceKey] = $.extend(true, {}, sourceItem);
}
});
}
});
aSelectedDataSet.push(oPushedObject);
});
It is unclear what exactly your fnCreateEnt() function returns though. I am assuming it is the populated oPushObj but it's not entirely clear from your question.
I have a main object consisting of two main properties, data which contains messages and included which contains the senders of the messages. I want to create a new Array called messages which will contain all the values of both objects but in a way that every object inside this array will consist of the data values adding the correct sender as property to each of them.
I am able to separate the main object to two different ones, one containing the data and the other containing the senders.
if (jsonAPI.data) {
$.each(jsonAPI.data, function(index, value) {
dataObj[index] = value;
});
}
if (jsonAPI.included) {
$.each(jsonAPI.included, function(index, value) {
senders[value.id] = value;
});
}
I guess I have to make an iteration for every value of the dataObj and check if the relationships.sender.data.id is equal to senders.id then add the new property to dataObj, but I don't know how to write it.
What I say can be more clear in this fiddle https://jsfiddle.net/mosmic/f2dzduse/
Working jsfiddle: https://jsfiddle.net/f2dzduse/5/
var jsonAPI = {<snip>};
var dataObj = {};
if (jsonAPI.data) {
$.each(jsonAPI.data, function(index, value) {
dataObj[index] = value;
});
}
$.each(dataObj, function(index, value) {
//Prevent error if there is no sender data in included
if(jsonAPI.included.length - 1 >= index) {
//check if ids are equal
if(value.relationships.sender.data.id == jsonAPI.included[index].id) {
value.sender = jsonAPI.included[index];
}
}
});
console.log(dataObj);
This code assumes that jsonAPI.data.relationships.sender.data.id and jsonAPI.included.id are both in the same order!
If this is not always the case let me know and I'll rewrite the code to loop trough each jsonAPI.data and then loop trough jsonAPI.include to check for an equal id. This code will be slower since it will loop a total of jsonAPI.data.length X jsonAPI.include times.
Here's the updated code: https://jsfiddle.net/f2dzduse/6/
var jsonAPI = {<snip>};
var dataObj = [];
$.each(jsonAPI.data, function(x, data) {
dataObj[x] = data;
$.each(jsonAPI.included, function(y, included) {
if(data.relationships.sender.data.id == included.id) {
dataObj[x].sender = included;
}
});
});
console.log(dataObj);
I've been trying to work with Trello and the Google Apps Script this week. I am trying to create an array of hashes that I can then use to load the spreadsheet. Google apps script doesn't like the typical javascript code of creating hashes. I've looked up the docs but they don't have anything like hashes...they say to:
var object = [];
var object1 = {};
object.push(object1);
This wont work because I'm essentially trying to do something like:
var hash={name: , label: };
var n= someNumber;
var l= someLabel
var hash.push(name: n, label: l);
Essentially that is the code I have right now. But here is my entire function:
function getData(){
var list={};
//get the list of delivered cards from Trello
var listRequest = authorizeToTrello(); // get authorization
var result = UrlFetchApp.fetch("https://trello.com/1/lists/4fea3a2c3a7038911ebff2d8/cards",
listRequest);//fetch list
var listOfCards = Utilities.jsonParse(result.getContentText());//Google app utility format json
//outer loop to iterate through list of Cards
for(var i=0; i < listOfCards.length; i++){
var cardId = listOfCards[i].id; //get the id of a single card
var l = listOfCards[i]["label"]; //get the label for the our structure
//get a json object for a single card within the list of cards iteration
var cardRequest = authorizeToTrello();
var getCard = UrlFetchApp.fetch("https://trello.com/1/cards/" + cardId + "/actions", cardRequest);
var singleCard = Utilities.jsonParse(getCard.getContentText());
//inner loop to iterate the single cards JSON objects
for(var j=0; j < singleCard.length; j++) {
if(singleCard[j].data != undefined && singleCard[j].data.listAfter != undefined)
{
var str = singleCard[j]["data"]["listAfter"]['name'];
if(str === "Delivered Q3 2012"){
var n = singleCard[j]['memberCreator']['fullName'];
}
}
}
//push the data to list
list.push(n,l);
}
return name, label; //return list for output
}
Reading the question, I understood that the author needs to know how to create an associative array in a GAS. If it is correct then here is a couple of links (here and here) and a sample code is bellow.
function testMap() {
var map = {};
map["name1"] = "value1";
map["name2"] = "value2";
return map;
}
If the author needs really
an array of hashes
then there are a couple of ways depending on which hash algorithm is required.
to use the Utilities.computeDigest method to calculate a hash of a string using one of available algorithms.
if the required hash calculation algorithm is not supported by the Utilities.computeDigest, then is possible to write own implementation as it is done for the BLAKE function.
Here is a sample of how to create an array of hashes using the MD5 hash.
function testHash() {
var array = [];
array.push(Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, "value1"));
array.push(Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, "value2"));
return array;
}
P.S. The return line of the author code return name, label; //return list for output
is not correct - only the label variable value is returned. To return a couple of variables as an array is necessary to write return [name, label];. Or may be the author needs to return the list variable and not name and label.
I know this is an old post / question, but i would like to update my answer since the original anwer (1st answer) is misleading. I was myself looking for how to return associative arrays back to a cell in the spreadsheet, but alas.. "YOU CANNOT". Google spreadsheet MUST want an numerically indexed array or an object. Otherwise it returns "#ERROR".
Here are the steps to replicate the issue.
function testMap() {
var map = {};
map["name1"] = "value1";
map["name2"] = "value2";
return map
Formula in your cell: =testMap()
Value in your cell: Thinking... #ERROR
Solution (rather a workaround)
1: Transfer your objects from your associative array into a numerically indexed array using for-each type loop.
var temp = new Array();
for (var i in map) {
temp.push([i,map[i]])
// optionally use activeSheet.getRange(X:X).setValue([i,map[i]])) function here.
// set values will not work in cell functions. To use it via cell functions, rerun / trigger the functions using an on_edit event.
}
If you used a temp like numerically indexed array, you can return "temp" back to the calling cell.
Summary: For onEdit() purposes, use Cache Service to define associative array data.
Here's a shared Gsheet demonstrating this curious behavior. I tried the following solution in programmatically defining an associative array based on data in a Google sheet.
var assocArr = {
labels: {},
init: function () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheetName');
var values = sheet.getDataRange().getValues();
for(var row in values) {
assocArr.labels[values[row][0]] = values[row][1];
};
for(var key in assocArr.labels) {
Logger.log("key: %s, value: %s",key, assocArr.labels[key]);
};
return(void(0));
},
};
To execute this, you run the init() method in the onOpen() event handler.
function onOpen() {
assocArr.init();
var key = 'test';
SpreadsheetApp.getUi().alert( assocArr.labels[key] );
Logger.log("onOpen: key: %s, value: %s",key, assocArr.labels[key]);
};
The logger message confirms that init() loads the data from the worksheet.
Now if I try to reference this assocArr object in onEdit() it returns undefined for all key values.
function onEdit(event) {
var key = 'test';
SpreadsheetApp.getUi().alert( assocArr.labels[key] );
Logger.log("onEdit: key: %s, value: %s",key, assocArr.labels[key]);
};
I infer that for security reasons, Google limited the simple-trigger onEdit() to not have global variable scope, same as they voided the utility of the event.user property.
Now instead if I simply put the key-value pair in the cache, it works! Here is the complete code that works using the Cache Service.
var cache = CacheService.getPrivateCache();
var assocArr = {
init: function () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Account Labels');
var values = sheet.getDataRange().getValues();
for(var row in values) {
cache.put(values[row][0], values[row][1], 3600);
};
return(void(0));
},
};
function onOpen() {
assocArr.init();
var key = 'test';
SpreadsheetApp.getUi().alert( cache.get(key) );
Logger.log("onOpen: key: %s, value: %s",key, cache.get(key));
};
function onEdit(event) {
var key = 'test';
SpreadsheetApp.getUi().alert( cache.get(key) );
Logger.log("onEdit: key: %s, value: %s",key, cache.get(key));
};
Curiously, the onEdit() has the cache variable in its scope.
Here again is the shared Gsheet demonstrating this curious behavior.
I found this really quick way that is not listed
Create a json object (array style)
var myArray = {
1:{"id": "inprogress","title" : "in Progress"},
2:{"id": "notstarted","title" : "Not Started"},
3:{"id": "completed" ,"title" : "Completed"}
};
read the json
// get the lenght of the json object
var jsonSize = Object.keys(myArray).length;
// use this in a loop
for (var i = 1; i < Object.keys(jsonSize).length; i++) {
var title = myArray[i].title;
}
Works like a charm for me
Hi I have an application that I have got half working. I have an array of objects, each with their properties already set and can call them like this myarray[i].property. I have an if statment that searchs through the array, within a loop, and pulls out any where myarray[i].property == my var.
The issue I'm having is that I want to put these results into a new array, built by the if statment/loop combo that searchs the first array, and I can't make it work.
This is what I have tried, but failed with?
var c = 0;
var matches = new Array('application', 'sclass', 'type', 'motor', 'bearings', 'gears', 'modelno', 'name', 'speed', 'v3_3', 'v4_8', 'v6_0', 'v7_2', 'weight', 'diensions', 'opvoltage', 'image', 'description');
//loop through servos array and pull any servo that has a matching application value to that selected by the search filter
for(var i=0; i < servos.length; i++){
if servos[i].application == document.searchFilters.applicationMenu.value) {
//populate the new 'matches' array with the details from the servos pulled from the inital arary
matches[c] = new servo(servos[i].application, servos[i].sclass, servos[i].type, servos[i].motor, servos[i].bearings, servos[i].gears, servos[i].modelno, servos[i].name, servos[i].speed, servos[i].v3_3, servos[i].v4_8, servos[i].v6_0, servos[i].v7_2, servos[i].weight, servos[i].dimensions, servos[i].opvoltage, servos[i].image, servos[i].description);
c++;
} else if (document.searchFilters.applicationMenu.value == 0){
//sets the value of servoDtore locally
var servoStore = 0;}
Further in the code I have the line document.getElementById('servoDisplay').innerHTML = "search result " + matches[c].modelno; //display servos model numbers stored within the matches array
Where am I going wrong, why do I always get '.modelno is null or undefined' errors whenever I try to call matches[c].modelno?
Let me try. Please tell me if I understood you incorrectly. I have modifyed your JS code to the following:
var matches = ['application', 'sclass', 'type', 'motor',
'bearings', 'gears', 'modelno', 'name', 'speed',
'v3_3', 'v4_8', 'v6_0', 'v7_2', 'weight',
'dimensions', 'opvoltage', 'image', 'description'],
output = [],
modelnos = [];
// c variable is unnecessary now
// Loop through servos array and pull any servo that has a matching application value to that selected by the search filter
for(var i = 0, len = servos.length; i < len; i+= 1) {
if (document.searchFilters.applicationMenu.value === servos[i].application) {
// Populate the new 'matches' array with the details from the servos pulled from the inital arary
var newEntry = new servo(servos[i].application, servos[i].sclass, servos[i].type, servos[i].motor,
servos[i].bearings, servos[i].gears, servos[i].modelno, servos[i].name, servos[i].speed,
servos[i].v3_3, servos[i].v4_8, servos[i].v6_0, servos[i].v7_2, servos[i].weight,
servos[i].dimensions, servos[i].opvoltage, servos[i].image, servos[i].description);
output.push(newEntry);
modelnos.push(newEntry.modelno);
// c++;
} else if (document.searchFilters.applicationMenu.value === 0) {
var servoStore = 0;
}
}
// Display servos model numbers stored within the matches array
document.getElementById('servoDisplay').innerHTML = "Search result: " + modelnos.join('<br />');