In my react app, I have two JSON objects that contain user quiz results. My goal is to generate a percentage value of how many answers each quiz in the roomSurveys has in common with the userSurvey answers using some sort of loop within a loop.
const [userSurvey, setUserSurvey] = useState({ "Q1":"A",
"Q2":"B, C, A",
"Q3":"C" });
const [roomSurveys, setRoomSurveys] = useState({
"user2":{ "Q1":"A",
"Q2":"B, C, C",
"Q3":"B" },
"user3":{ "Q1":"C",
"Q2":"B",
"Q3":"B" },
});
The function that calculates the answers in common would then update a state like this:
const [roomMatches, setRoomMatches] = useState({
"user2":{ "percentMatch":"1.0"},
"user3":{ "percentMatch": ".30"}
})
here is what I've got so far:
//loop through each user survey in the roomSurvey object
for (var key of Object.keys(roomSurveys)) {
var countSame;
var countTotal;
//loop through each user survey within the roomSurvey object
for (var key of Object.keys(roomSurveys[key])) {
//if answer matches userSurvey answer for the same key,
//add to count same and count total else, add to count total
if(roomSurveys[key] == userSurvey[key]){
countSame++;
countTotal++;
}else{
countTotal++;
}
console.log(key + " -> " + roomSurveys[key])
console.log(key + " -> " + userSurvey[key])
}
var percentMatched = countSame/countTotal;
setRoomMatches([...roomMatches, {key:{['percentMatch']: percentMatched}}]);
}
here are the errors I am getting:
In the second for loop, the roomSurveys[key] value is always undefined
roomMatches is not iterable (when trying to add new match percent to roomMatches state)
You have ovewritten your key variable int the second for loop. You need to name them key1 and key2 (or something more verbose)
You're using the same name for key variables, rename them and use them as desired.
... V
for (var survey of Object.keys(roomSurveys)) {
var countSame;
var countTotal;
v
for (var room of Object.keys(roomSurveys[survey])) {
...
Related
I have the following code which works:
ctx.load(workflowAssociations);
//below works but loops through all available workflows, it seems sub may contain the array that needs to be accessed
ctx.executeQueryAsync(function (sender, args) {
var subsEnum = workflowAssociations.getEnumerator();
while (subsEnum.moveNext()) {
var sub = subsEnum.get_current();
alert(sub);
console.log('Web: ' + web.get_url() + ', Subscription: ' +
sub.get_name() + ', id: ' + sub.get_id());
var initiationParams = {};
workflowServicesManager.getWorkflowInstanceService().startWorkflowOnListItem(
sub, items.getItemAtIndex(0).get_id(), initiationParams);
ctx.executeQueryAsync(function (sender, args) {
console.log('Workflow started.');
}, errFunc);
}
}, errFunc);
function errFunc(sender, args) {
alert("Error occured! " + args.get_message() +
'\r\nStack trace: ' + args.get_stackTrace());
}
I am trying to simplify this loop and only access one object in the collection without the while loop. I have tried the following:
//var subsEnum = workflowAssociations.getEnumerator();
console.dir(subsEnum);
console.dir(workflowAssociations[2]);
console.log(subsEnum[2]);
var sub = subsEnum.get_current(0);
console.dir(sub);
console.dir(subsEnum);
However, most of these come up undefined. Here is an image of what it looks like when I explore the object using the watch expression ability in chrome.
I dont want to use that $2_0 thing because in migration I assume it may change.
I apologize in advance for any lack of information.
SP.Workflow.WorkflowAssociation class (workflowAssociations in your example) derives from SP.ClientObjectCollection class which in addition to getEnumerator() method contains some additional methods for accessing collection data such as:
getItemAtIndex - gets the element at the specified index of the SP.ClientObjectCollection class.
getData - transforms ClientObjectCollection object to regular JavaScript array
Examples
Enumerate collection
var e = workflowAssociations.getEnumerator();
while(e.moveNext()){
console.log(e.get_current().get_name()); //print Name of current WorkflowAssociation object
}
Get item at the specified index
var item = workflowAssociations.getItemAtIndex(0); //get first item
console.log(item.get_name()); //print Name of WorkflowAssociation object
Convert to array
var items = workflowAssociations.get_data();
console.log(items[0].get_name()); //print Name of first WorkflowAssociation object
I am very new with Javascript and I can't seem to find an explanation for what is happening with my code.
I want to create an array of "people" where each person has some information associated with them, like "id" and "name". I don't know how many "people" I would need in my array so I am using "push" when I need another person. My problem is my array ends up filled with the last person's information.
Here is my declarations that I am using:
var ppl_arr = [];
var profile = {
id: 10000,
name: " ",
};
profile.id=3;
ppl_arr.push(profile); //add this person to my array
alert(ppl_arr[0].id + "\t" + ppl_arr.length);
profile.id=5;
ppl_arr.push(profile); // push next person to the array
alert(ppl_arr[0].id+"\t"+ppl_arr[1].id + "\t"+ppl_arr.length);
The first alert displays correctly : "3 1"
In the second alert, I get " 5 5 2" instead of " 3 5 2"
So I get two entries into my array but the second one seems to overwrite the first one. Can anyone explain what is happening?
You are simply changing the id of the same object, and adding the same object to the array twice. I would suggest that you create your 'people' objects as instance objects, something like this
//This is a constructor function for a Person object
function Person(id,name)
{
this.Id = id;
this.Name = name;
}
then
var ppl_arr = [];
ppl_arr.push(new Person(3,"Bob")); //add this person to my array
alert(ppl_arr[0].Id + " - " + ppl_arr.length); //outputs "3 - 1"
//NOTE put a string inbetween, because Id and length are both integers,
//you would actual get a sum of the two, not a string concatenation.
ppl_arr.push(new Person(5,"Steve")); // push next person to the array
alert(ppl_arr[0].Id+"\t"+ppl_arr[1].Id + "\t"+ppl_arr.length); // outputs 3 5 2
Question #1:
alert(ppl_arr[0].id + ppl_arr.length); will display the sum, not the concatenation - try alert(ppl_arr[0].id.toString().concat(ppl_arr.length));
Question #2:
You change the id property of an existing object, not copy it. So you change the id of the object already in the array as well. So you would need to
var ppl_arr = [];
var profile = {
id: 10000,
name: " ",
};
profile.id=3;
ppl_arr.push(profile);
//Create a new profile
var profile2 = {
id: 10000,
name: " ",
};
profile2.id=5;
ppl_arr.push(profile2);
Little change of my code and it's partially works :)
var db = {
hotels: JSON.parse(localStorage.getItem('table') || "[]"),
get objects() {return this.hotels},
set objects(obj) {
obj = this.hotels;
localStorage.setItem('table', JSON.stringify(obj))
}
}
jQuery(function(){
var count = localStorage.getItem('count');
if(!count) {
count = 0;
}
function Add(item){
var client = {
ID : jQuery(item).find("#txtID").val(),
Name : jQuery(item).find("#txtName").val(),
Photo : jQuery(item).find("#txtPhone").val(),
Link : jQuery(item).find("#txtEmail").val()
};
db.objects = db.objects.push(client);
count = count+1;
localStorage.setItem('count',count);
jQuery('.panel2 a span').text('('+ localStorage.getItem('count') +')');
jQuery(item).find('.add_rem').hide();
jQuery(item).find('.remove').show();
jQuery("#tblList .empty").hide();
jQuery("#tblList").find('li:gt(0)').remove();
jQuery.each(db.objects,function(i,element) {
jQuery("#tblList").append("<li class='added"+db.objects[i].ID+"'>"+
"<img src='../../images/general/delete.gif' alt='Delete"+i+"' class='delete'/>" +
"<a href='"+db.objects[i].Link+"' title='"+db.objects[i].Name+"'>"+
" <img src='"+db.objects[i].Photo+"' alt='"+db.objects[i].Name+"'>" +
" <span>"+db.objects[i].Name+"</span>" +
" </a>" +
"</li>");
})
return true;
}
function Delete(item){
jQuery(item).prev('.add_rem').show();
jQuery(item).find('.remove').hide();
jQuery(item).find('.remove').removeAttr('alt');
}
function List(){
if(count > 0) {
jQuery("#tblList .empty").hide();
jQuery('.panel2 a span').text('('+ localStorage.getItem('count') +')');
}
for(var i= 0; i<= count; i++) {
var cli = JSON.parse(db.hotels);
if(cli[i] != null){
jQuery("#"+cli[i].ID).find('.add_rem').hide();
jQuery("#"+cli[i].ID).find('.remove').show();
jQuery("#"+cli[i].ID).find('.remove').attr('alt','Delete'+i);
jQuery("#tblList").append("<li class='added"+cli[i].ID+"'>"+
"<img src='../../images/general/delete.gif' alt='Delete"+i+"' class='delete'/>" +
"<a href='"+cli[i].Link+"' title='"+cli[i].Name+"'>"+
" <img src='"+cli[i].Photo+"' alt='"+cli[i].Name+"'>" +
" <span>"+cli[i].Name+"</span>" +
" </a>" +
"</li>");
}
}
}
jQuery("#frmCadastre").bind('submit',function(e){
e.preventDefault()
return Add(this);
});
List();
jQuery(".remove, .delete").bind("click", function(e){
e.preventDefault();
Delete(this);
List();
});
})
now my question is how to push element to array after page refresh this is located in function Add()
array looks like this
"["{"ID":"1","Name":"test","photo":"/link/to/photo.jpg"}"]"
and if i add another element before page refresh it works great
"["{"ID":"0","Name":"test0","photo":"/link/to/photo0.jpg"}","{"ID":"1","Name":"test1","photo":"/link/to/photo1.jpg"}"]"
but if i Reload page and try to add an element Firebug is throwing:
`TypeError: db.objects.push is not a function
db.objects = db.objects.push(client);`
We will start to clear out what happens with HTML5 Local Storage. Local storage is a place in your disk defined by every browser that supports it. These places may be different for every browser. They hold tuples with keys and values, both strings. If you want to save a whole object to the disk, you must serialize it. That means you have to transform it into an array of data, in our case chars -> string. In javascript the most common object serialize function is JSON.stringify. Its input is a valid JSON object, which in our case is an array and it will make it into a string using the literals you use to initialize an object like {x:5}. JSON.stringify([{x:5},{x:6},{x:7}]) will have the following output: "[{x:5},{x:6},{x:7}]". And to reconstruct your object from a string you use JSON.parse(x) where x is a valid json string. You want now to have an array of objects, the first thing you'll think of is to serialize your array you have as a var in your program and add a special key you remember to store it into your disk. Each browser has seperate localStorage for every site that is hosted by a server.
An example that stores a value bound to a key in localstorage is this:
localStorage.setItem('x','5');
localStorage['x'] = 5;
localStorage.x = 5;
all of them do the same thing, and their speed is in descending order. Now you have at Chrome->resources->localstorage:
+-------------------+
| Key | Value |
+---------+---------+
| x | "5" |
+---------+---------+
When you make your first visit to the page, you have nothing in localStorage, so you must have some initial values. Trying to get a value by:
return localStorage.getItem('x');
return localStorage['x'];
return localStorage.x;
will give you undefined. There is a nice operator made in javascript and is the ||.
null || 5 //returns 5
undefined || 3.14 //returns 3.14
'' || 6 //returns 6
[] || {} //returns []
If the left operand "exists", return it, else return the right one. This makes things faster with localStorage.getItem('x') || 5 so if a tuple with key x exists it will return the value of the item with the specified key, else it will return the 5, which is our initial value.
Let's get back to the localStorage again. Remember the tuples are saved into the disk, which is vastly slower to access than things in ram. If I want to read the value of an item in the localStorage let say in a loop, several times, should I read it directly from the disk, or should I read it once from the disk and save it into ram to access it faster? You surely know what makes more sense...So I must have a variable that is the clone of the one in the localStorage. Let's say I name it private_var. It must have an initial value which will be:
var private_array = JSON.parse(localStorage.getItem('array')) || [];
When you want to change your array in localstorage (e.g pushed an item) you use:
private_array.push(item)
localStorage.setItem('array', JSON.stringify(private_array))
Your localstorage will be like:
+---------+-----------------------------------------+
| Key | Value |
+---------+-----------------------------------------+
| array | [{"name":"george", "surname":"bush"}] |
+---------+-----------------------------------------+
To make things faster in terms of code production, not program speed you can define setters and getters.
var obj = {
temp: 5,
get x( ) { return this.temp },
set x(value) { this.temp = value }
}
we have an object named obj, a member temp and a setter and a getter function, just like in some java code. You can check obj.temp === 5. These special operators allow us to write
obj.x = obj.x + 6;
and it will be executed as something like this:
obj.set_x(obj.get_x() + 6);
So let's say you have an interface named db (database, similar to what a locastorage is) and a "private member" which is clearly not. You can see an implementation at http://ejohn.org/blog/javascript-getters-and-setters/ with __define(G/S)etter__ which has real private member, but this one is surely faster to write and more readable.
var db = {
cl_arr: JSON.parse(localStorage.getItem('array')) || [],
get clients( ) { return this.cl_arr },
set clients(v) {
localStorage.setItem('array', JSON.stringify(this.cl_arr));
if(v.constructor === Array) { this.cl_arr = v }
}
}
so when I execute:
db.clients.filter(function(client) { return client.money > 1000 });
this will be executed
db.get_clients().filter...;
And when I try to change the array, I will write this
db.clients = db.clients.push(client);
Even if the push method can change the array, only the get function will be triggered so only the "private" cl_arr variable will change, to trigger the setter which updates our localStorage too I have to write the db.clients = ...
I am trying to build a data structure.
In my limited knowledge, 'hash table' seems to be the way to go. If you think there is an easier way, please suggest it.
I have two, 1-dimensional arrays:-
A[] - contains names of badges (accomplishment)
B[] - contains respective dates those achievements were accomplished from array A[].
An achievement/accomplishment/badge can be accomplished more than one time.
Therefore a sample of the two arrays:-
A['scholar', 'contributor', 'teacher', 'student', 'tumbleweed', 'scholar'.....,'scholar',......]
B['1/2010', '2/2011', '3/2011', '6/2012', '10/2012', '2/2013',......'3/2013',........]
What I want to achieve with my data structure is:-
A list of unique keys (eq:- 'scholar') and all of its existing values (dates in array B[]).
Therefore my final result should be like:-
({'scholar': '1/2010', '2/2013', '3/2013'}), ({'contributor' : ........})..........
This way I can pick out a unique key and then traverse through all its unique values and then use them to plot on x-y grid. (y axis labels being unique badge names, and x axis being dates, sort of a timeline.)
Can anyone guide me how to build such a data structure??
and how do I access the keys from the data structure created.... granted that I don't know how many keys there are and what are their individual values. Assigning of these keys are dynamic, so the number and their names vary.
Your final object structure would look like this:
{
'scholar': [],
'contributor': []
}
To build this, iterate through the names array and build the final result as you go: if the final result contains the key, push the corresponding date on to its value otherwise set a new key to an array containing its corresponding date.
something like:
var resultVal = {};
for(var i = 0; i < names.length; ++i) {
if(resultVal[names[i]]) {
resultVal[names[i]].push(dates[i]);
} else {
resultVal[names[i]] = [dates[i]];
}
}
Accessing the result - iterating through all values:
for(var key in resultVal) {
var dates = resultVal[key];
for(var i = 0; i < dates.length; ++i) {
// you logic here for each date
console.log("resultVal[" + key + "] ==> " + resultVal[key][i]);
}
}
will give results like:
resultVal[scholar] ==> 1/2010
resultVal[scholar] ==> 2/2013
resultVal[scholar] ==> 3/2013
resultVal[contributor] ==> 2/2011
resultVal[teacher] ==> 3/2011
resultVal[student] ==> 6/2012
resultVal[tumbleweed] ==> 10/2012
You can try this...
var A = ['scholar', 'contributor',
'teacher', 'student', 'tumbleweed', 'scholar','scholar'];
var B = ['1/2010', '2/2011',
'3/2011', '6/2012', '10/2012', '2/2013','3/2013'];
var combined = {};
for(var i=0;i<A.length;i++) {
if(combined[A[i]] === undefined) {
combined[A[i]] = [];
}
combined[A[i]].push(B[i]);
}
Then each one of the arrays in combined can be accessed via
combined.scholar[0]
or
combined['scholar'][0]
Note the === when comparing against undefined
I have a servlet which talks with the database then returns a list of ordered (ORDER BY time) objects. At the servlet part, I have
//access DB, returns a list of User objects, ordered
ArrayList users = MySQLDatabaseManager.selectUsers();
//construct response
JSONObject jsonResponse = new JSONObject();
int key = 0;
for(User user:users){
log("Retrieve User " + user.toString());
JSONObject jsonObj = new JSONObject();
jsonObj.put("name", user.getName());
jsonObj.put("time", user.getTime());
jsonResponse.put(key, jsonObj);
key++;
}
//write out
out.print(jsonResponse);
From the log I can see that the database returns User objects in the correct order.
At the front-end, I have
success: function(jsonObj){
var json = JSON.parse(jsonObj);
var id = 0;
$.each(json,function(i,item) {
var time = item.time;
var name = item.name;
id++;
$("table#usertable tr:last").after('<tr><td>' + id + '</td><td width="20%">' + time +
'</td><td>' + name +
'</td></tr>');
});
},
But the order is changed.
I only noticed this when the returned list has large size (over 130 users).
I have tried to debug using Firebug, the "response tab" in Firebug shows the order of the list is different with the log in the servlet.
Did i do anything wrong?
EDIT: Example
{"0":{"time":"2011-07-18 18:14:28","email":"xxx#gmail.com","origin":"origin-xxx","source":"xxx","target":"xxx","url":"xxx"},
"1":{"time":"2011-07-18 18:29:16","email":"xxx#gmail.com","origin":"xxx","source":"xxx","target":"xxx","url":"xxx"},
"2":
,...,
"143":{"time":"2011-08-09 09:57:27","email":"xxx#gmail.com","origin":"xxx","source":"xxx","target":"xxx","url":"xxx"}
,...,
"134":{"time":"2011-08-05 06:02:57","email":"xxx#gmail.com","origin":"xxx","source":"xxx","target":"xxx","url":"xxx"}}
As JSON objects do not inherently have an order, you should use an array within your JSON object to ensure order. As an example (based on your code):
jsonObj =
{ items:
[ { name: "Stack", time: "..." },
{ name: "Overflow", time: "..." },
{ name: "Rocks", time: "..." },
... ] };
This structure will ensure that your objects are inserted in the proper sequence.
Based on the JSON you have above, you could place the objects into an array and then sort the array.
var myArray = [];
var resultArray;
for (var j in jsonObj) {
myArray.push(j);
}
myArray = $.sort(myArray, function(a, b) { return parseInt(a) > parseInt(b); });
for (var i = 0; i < myArray.length; i++) {
resultArray.push(jsonObj[myArray[i]]);
}
//resultArray is now the elements in your jsonObj, properly sorted;
But maybe that's more complicated than you are looking for..
As mentioned by ghayes , json objects are unordered.
There are multiple solutions to this problem.
You can use array and the sort it to get the ordered list.
You can use gson library to get the desired order of elements.
I would prefer the second option as it is easy to use.
As JSONObject is order less and internally uses Hashmap. One way to use it to download the all classes from org.json and use in your project directly by changing the internal HashMap implementation to LinkedHashMap in JSONObject.java file. below is the sorted json files
https://github.com/abinash1/Sorted-Json-Object