Sum of data from a firebase document - reactjs

Firebase dp data sum
I have a web application and I would like to make a function to add all the integers in the "equipment" field of all the documents in the "epis" collection.
My application is in reactjs.
What's the best way?
enter image description here

I believe this is a pure JS problem. My solution would be to use the JS reduce function on the array of objects after fetching from firestore. i.e.
function getSum(total, num) {
return (total + parseInt(num.equipment));
}
useEffect(() => {
var firestoreArray = [...]; //this is populated with the array of objects
// from firestore when you fetch. format is {name: "shovel", equipment: "12" }
var total = firestoreArray.reduce(getSum, 0); //reduce function where the 0 is the starting
//point of the sum. the getSum function is adding by parsing to Integer first for all the
//objects "equipment" attribute.
}, [firestoreArray]}

Related

Write data in to nested object in firebase firestore

I have a data structure that looks as follows:
This is the top level of the collection:
I want to write to increment the field count but I can't do it. I've tried so many different methods that I'd rather not go through all of them. The closest I've gotten was through:
const pageRef = admin.firestore().collection("pages").doc(image.page);
pageRef.set(
{
[`images.${image.position}.count`]: admin.firestore.FieldValue.increment(
1
),
},
{ merge: true }
);
But that leaves me with:
Please help. Changing the structure of pages is an option.
This is what I've tried to replicate:
Update fields in nested objects in firestore documents?
The issue is on how the point notaition is being used.
In the Post you shared the example they use is:
var setAda = dbFirestore.collection('users').doc('alovelace').update({
"first.test": "12345"
});
Applying this to your Code and model would be:
const pageRef = admin.firestore().collection("pages").doc(image.page);
pageRef.set(
{
`images[${image.position}].count`: admin.firestore.FieldValue.increment(
1
),
},
{ merge: true }
);
This will affect the element in the Array Images, the element image.position its value count.

LocalStroage: Count number of items in local storage and display it in UI using AngularJS

I have a list of products and i'm storing it in my LocalStorage. My LocalStroage looks something like this:
{
"2":{"id":"2","colour":"Black","size":"S","quantity":"1"},
"3":{"id":"3","colour":"Blue","size":"Universal","quantity":"1"},
}
I want to display '2' in my UI(because i have two items in my LocalStorage). What I tried so far is:
var temp = JSON.parse(localStorage["productTable"]);
for(var i in temp) {
totalitems ++;
}
itemsincart.innerHTML = '<i class=\"ion-android-cart\"></i><span id=\"cart-total\">'+totalitems+'</span>';
In this i'm getting totalitems as sum of id which I know is wrong.
And tried this Localstorage: Count how many values in key when using stringify and in this i'm getting '1' everytime.
Can anyone please help me figure out how I can show the number of items in my LocalStorage?
You do not need to use for loop Object.keys(temp).length to get the length of json object like following snippet:
var temp = {
"2":{"id":"2","colour":"Black","size":"S","quantity":"1"},
"3":{"id":"3","colour":"Blue","size":"Universal","quantity":"1"},
};
console.log(Object.keys(temp).length+" items in cart");
In your case try following:
var temp = JSON.parse(localStorage["productTable"]);
itemsincart.innerHTML = '<i class=\"ion-android-cart\"></i><span id=\"cart-total\">'+Object.keys(temp).length+'</span>';
I recently searched a solution for this, and found out the easiest way is just to create another localStorage item (cartQty).
So everytime you add a product to your cart, increase the cartQty number.
$('button').on('click',function() {
//...
++i;
window.localStorage.setItem('cartQty', i);
// display your cart quantity
$('span#total').text(window.localStorage.getItem('cartQty');
}
didn't realised question is for AngularJS, but you get the idea
Try this out,
var totalitems = JSON.parse(localStorage["productTable"]).length;
itemsincart.innerHTML = '<i class=\"ion-android-cart\"></i><span id=\"cart-total\">'+totalitems+'</span>';

AngularJS - wait for data from server and add them correctly to list

I am currently working on an AngularJS project in which I have to get project information, for a specific month and year, from the server and show them to the user.
First of all I'm getting a list of Project Id's (projectList), which can be variable, and then I need to get the info on those projects for a specific year and month. With this code I'm trying to get the data and to refresh the data when the last projects is successful. After the data is fetched, I use a ng-repeat to show it to the user.
$scope.getData = function(){
$scope.projectInfoList = [];
for(var index=0; index < $scope.projectList.length; index++){
projectService.getProject($scope.model.year, $scope.model.month, parseInt($scope.projectList[index]) ).success(function(data){
var listInput = { projectID : $scope.projectList[index], data : data};
$scope.projectInfoList.push(listInput);
if(index == $scope.projectList.length - 1){
$scope.$apply();
}
});
};
}
This has 2 mistakes.
It adds only data to the last index.
It doesn't refresh the data immediately when I request data for another month or year
I have looked for solutions with $q.all but I'm not sure how I would use it together with variable amount of functions of 'projectService.getProject(..)'
The anonymous callback that you give to the function success use a closure to the variable index.
But, your anonymous callback will be called asynchronously (when the call will be done). So when it will be called when index will be the last index of the array (so here $scope.projectList.length - 1).
To avoid that, you can use the following pattern:
for(var index=0; index < $scope.projectList.length; index++){
(function (index) {
projectService.getProject($scope.model.year, $scope.model.month, parseInt($scope.projectList[index]) ).success(function(data){
var listInput = { projectID : $scope.projectList[index], data : data};
$scope.projectInfoList.push(listInput);
if(index == $scope.projectList.length - 1){
$scope.$apply();
}
});
})(index)
}
Your second mistake is probably because you change the reference of the array projectInfoList in your function with $scope.projectInfoList = [];.
Take a look at this post for more details on this last problem: ng-repeat not updating on update of array

Appending Class Objects to Array [Swift]

I have a RestAPI that pulls JSON data from a webpage, and because I want to follow MVC programming standards, I want to fetch this data through a "Data List" class that instantiates the RestAPI Class.
I have a class called "Card", which provides the basic structure of a type of data object. I have a class called "CardList" whose initialization creates an Array of the "Card" Classes.
However, I'm having trouble appending Card Class data to my CardList Array.
Here is the code for CardList Class:
class CardList {
var cards: [Card]
static var sharedInstance = CardList()
//MARK - Initalize
private init(){
//Dummy Data
let helm = Card(name: "Helm of Testing", cost: 10, type: "Equipment", subType: "Head Armor", description: "Some say the Helmet of Testing helps keep it's wearers mind clear.")
//Add to Array
var c = [helm]
let items: NSMutableArray = []
//Get API Array
RestApiManager.sharedInstance.getElements { (json: JSON) in
let results = json["results"]
for (_, subJson) in results {
let card:AnyObject = subJson["card"].object
items.addObject(card)
}
for var i = 0; i < items.count; ++i{
let data:JSON = JSON(items[i])
let newCard = Card(name: data["title"].stringValue, cost: Int(data["cost"].string!)!, type: data["type_id"].stringValue, subType: data["subtype_id"].stringValue, description: data["description"].stringValue)
c.append(newCard)
}
}
cards = c.sort { $0.cost < $1.cost }
}
//END Class
}
So basically on initialization, this class creates a dummy Card, adds it to an array called "c". An array called "items" is created. There RestApiManager returns JSON data, for every type of data returned in the results, it gets added into the "items" array.
We then loop through the "items" array, reading it as JSON (I'm using the SwiftyJSON plugin/code snip) and creating a new "Card" Class for each item in the array. We then append the "Card" to the "c" Array.
Finally we take the Card Array "cards" and set it equal to the "c" Array sorted by it's value cost.
The Problem:
After running the code, the "cards" Array only returns the dummy card named "helm", and none of the data added to the "c" array.
Notes:
Yes, my Api is working, if I print the values in the "for var i = 0" loop, it is printing correct values. If I print the "c.count" in the same loop, I get two (The API is only returning 1 other data set, or "Card"). If I print the "c.count" outside of that loop, after the loop, it says there is only 1 item in the Array when there should be two (the dummy data and the data returned from the JSON call).
So, it's clearly something with my syntax. Or maybe I'm making things more complicated than they need to. I'm fairly new to Swift and slightly confused. I just need someone else to look at my code.
Thank you in advance.
RestApiManager.sharedInstance.getElements works asynchronously.
The block containing the data is executed after the init function exits.
Move the code to assign and sort the array into the block.
...
for var i = 0; i < items.count; ++i{
let data:JSON = JSON(items[i])
let newCard = Card(name: data["title"].stringValue, cost: Int(data["cost"].string!)!, type: data["type_id"].stringValue, subType: data["subtype_id"].stringValue, description: data["description"].stringValue)
c.append(newCard)
}
cards = c.sort { $0.cost < $1.cost }
}
}

In Firebase, is there a way to get the number of children of a node without loading all the node data?

You can get the child count via
firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });
But I believe this fetches the entire sub-tree of that node from the server. For huge lists, that seems RAM and latency intensive. Is there a way of getting the count (and/or a list of child names) without fetching the whole thing?
The code snippet you gave does indeed load the entire set of data and then counts it client-side, which can be very slow for large amounts of data.
Firebase doesn't currently have a way to count children without loading data, but we do plan to add it.
For now, one solution would be to maintain a counter of the number of children and update it every time you add a new child. You could use a transaction to count items, like in this code tracking upvodes:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
For more info, see https://www.firebase.com/docs/transactions.html
UPDATE:
Firebase recently released Cloud Functions. With Cloud Functions, you don't need to create your own Server. You can simply write JavaScript functions and upload it to Firebase. Firebase will be responsible for triggering functions whenever an event occurs.
If you want to count upvotes for example, you should create a structure similar to this one:
{
"posts" : {
"-JRHTHaIs-jNPLXOQivY" : {
"upvotes_count":5,
"upvotes" : {
"userX" : true,
"userY" : true,
"userZ" : true,
...
}
}
}
}
And then write a javascript function to increase the upvotes_count when there is a new write to the upvotes node.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});
You can read the Documentation to know how to Get Started with Cloud Functions.
Also, another example of counting posts is here:
https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js
Update January 2018
The firebase docs have changed so instead of event we now have change and context.
The given example throws an error complaining that event.data is undefined. This pattern seems to work better:
exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
const data = change.after.val();
const count = Object.keys(data).length;
return change.after.ref.child('_count').set(count);
});
```
This is a little late in the game as several others have already answered nicely, but I'll share how I might implement it.
This hinges on the fact that the Firebase REST API offers a shallow=true parameter.
Assume you have a post object and each one can have a number of comments:
{
"posts": {
"$postKey": {
"comments": {
...
}
}
}
}
You obviously don't want to fetch all of the comments, just the number of comments.
Assuming you have the key for a post, you can send a GET request to
https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.
This will return an object of key-value pairs, where each key is the key of a comment and its value is true:
{
"comment1key": true,
"comment2key": true,
...,
"comment9999key": true
}
The size of this response is much smaller than requesting the equivalent data, and now you can calculate the number of keys in the response to find your value (e.g. commentCount = Object.keys(result).length).
This may not completely solve your problem, as you are still calculating the number of keys returned, and you can't necessarily subscribe to the value as it changes, but it does greatly reduce the size of the returned data without requiring any changes to your schema.
Save the count as you go - and use validation to enforce it. I hacked this together - for keeping a count of unique votes and counts which keeps coming up!. But this time I have tested my suggestion! (notwithstanding cut/paste errors!).
The 'trick' here is to use the node priority to as the vote count...
The data is:
vote/$issueBeingVotedOn/user/$uniqueIdOfVoter = thisVotesCount, priority=thisVotesCount
vote/$issueBeingVotedOn/count = 'user/'+$idOfLastVoter, priority=CountofLastVote
,"vote": {
".read" : true
,".write" : true
,"$issue" : {
"user" : {
"$user" : {
".validate" : "!data.exists() &&
newData.val()==data.parent().parent().child('count').getPriority()+1 &&
newData.val()==newData.GetPriority()"
user can only vote once && count must be one higher than current count && data value must be same as priority.
}
}
,"count" : {
".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
newData.getPriority()==data.getPriority()+1 "
}
count (last voter really) - vote must exist and its count equal newcount, && newcount (priority) can only go up by one.
}
}
Test script to add 10 votes by different users (for this example, id's faked, should user auth.uid in production). Count down by (i--) 10 to see validation fail.
<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
window.fb = new Firebase('https:...vote/iss1/');
window.fb.child('count').once('value', function (dss) {
votes = dss.getPriority();
for (var i=1;i<10;i++) vote(dss,i+votes);
} );
function vote(dss,count)
{
var user='user/zz' + count; // replace with auth.id or whatever
window.fb.child(user).setWithPriority(count,count);
window.fb.child('count').setWithPriority(user,count);
}
</script>
The 'risk' here is that a vote is cast, but the count not updated (haking or script failure). This is why the votes have a unique 'priority' - the script should really start by ensuring that there is no vote with priority higher than the current count, if there is it should complete that transaction before doing its own - get your clients to clean up for you :)
The count needs to be initialised with a priority before you start - forge doesn't let you do this, so a stub script is needed (before the validation is active!).
write a cloud function to and update the node count.
// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.userscount = functions.database.ref('/users/')
.onWrite(event => {
console.log('users number : ', event.data.numChildren());
return event.data.ref.parent.child('count/users').set(event.data.numChildren());
});
Refer :https://firebase.google.com/docs/functions/database-events
root--|
|-users ( this node contains all users list)
|
|-count
|-userscount :
(this node added dynamically by cloud function with the user count)

Resources