Check and compare messages in Swift and Firebase - swift

How do I compare the responses of two people and calculate the percentage of similarity? For example, in my database below, Kevin answered questionId with "yes" and questionId1 with "no". John answered "yes" and "yes". I would like the output to show 50% given that they have the same answer for 1 and a different answer for the other.
I'm trying this but not sure how to compare other users:
func calculatePercentage(completion: #escaping ([String])->()) {
let postRef = self.databaseRef.child("responses").child("Kevin")
postRef.observeSingleEvent(of: .value, with: { (snapshot) in
var userIdArray = [String]()
for topic in snapshot.children.allObjects as! [DataSnapshot] {
let question1 = topic.childSnapshot(forPath: "questionId").value
let question2 = topic.childSnapshot(forPath: "questionId1").value
userIdArray.append(topic as! String)
}
completion(userIdArray)
})
}

fetch all responses that you want to compare as Dictionary or Arrays or Objects.
For more information and example please see firebase read_data document.
Compare each questions, it's same or not. then count the numberOfSameAnswers.
Like Joakim Danielson wrote, calculation is
percentageOfSameAnswer = (numberOfSameAnswers/numberOfAllQuestions)*100

Related

Accessing Firebase Data inside unique AutoID

This is my first question and I'm still learning Swift/Xcode/Firebase, so I appreciate your patience. I've been stalking StackOverflow and have found a lot of answers to help with various things, but nothing that makes sense for the problem I've been struggling with for 2 days.
I am writing a program that will save a date picked on a previous viewcontroller and a set of user-entered floats from text fields to a Firebase database, and append each data set as a separate entry instead of overwriting the previous data. Using the first block of code below, I've got this problem solved except I can't find a way to do it without using AutoID. This leaves me with a setup like this in Firebase, but with multiple categories and "optionSelected" sections in each category:
program-name
Category 1
optionSelected
L1cggMnqFqaJf1a7UOv
Date: "21-12-2017"
Variable 1 Float: "12345"
Variable 2 Float: "26.51"
L1ciVpLq1yXm5khimQC
Date: "30-12-2017"
Variable 1 Float: "23456"
Variable 2 Float: "35.88"
Code used to save:
func newWithNewVars() {
let myDatabase = Database.database().reference().child("Category 1").child(optionSelected)
let variable1 = textField1.text
let variable2 = textField2.text
let variable1Float = (textField1.text! as NSString).floatValue
let variable2Float = (textField2.text! as NSString).floatValue
let writeArray = ["Date": textPassedOverDate, "Variable 1 Float": variable1Float, "Variable 2 Float": variable2Float]
myDatabase.childByAutoId().setValue(gasArray) {
(error, reference) in
if error != nil {
print(error!)
}
else {
print("Message saved successfully!")
}
}
}
The problem comes with recalling data. Since the AutoID is unique, I can't figure out how to access the data deeper inside for calculations. Specifically, I want to be able to make a new entry, press the save data button, and have it find the most recent entry in the "optionSelected" section so it can do calculations like subtract the older variable 1 from the new variable 1 and such.
Given the above description, layout, and code used above, what code structure would allow me to find the most recent date and access the data inside the AutoID sections for a specific category and "optionSelected"?
Thank you for your help.
The issue you're having is that you're trying to dig deeper but can't as you don't have a hold of that id. You'll want to use the .childAdded in your reference observation when you want to get inside of a list in your JSON tree when you don't have a hold of that id to get inside - this will be called as many times as there are values inside of Category 1 tree:
let reference = Database.database().reference()
reference.child("Category 1").child("optionSelected").observe(.childAdded, with: { (snapshot) in
let uniqueKey = snapshot.key // IF YOU WANT ACCESS TO THAT UNIQUE ID
print(uniqueKey)
guard let dictionary = snapshot.value as? [String: AnyObject] else { return }
let date = dictionary["date"] as? String
let variableOne = dictionary["Variable 1 Float"] as? Float
let variableOne = dictionary["Variable 2 Float"] as? Float
}, withCancel: nil)
You may also want to avoid using spaces in your database keys to avoid any problems in the near future. I'd stick with the common lowercased underscore practice e.g. "category_1" or "variable_2_float"

swift firebase nested children count

groups
--group1(autoid)
---subgroup1(autoid)
----id
----ownerId
----description
--group2(autoid)
---subgroup2(autoid)
----id
----ownerId
----description
In a structure like over here i have to count all occurrencies of ownerId that are equal to my id (currentUserId) in all groups, can somebody help me?
what i've done so far:
root.child("groups").observe(.value, with: {(snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
var count = 0
for child in result {
let orderID = child.key as String //get autoID
self.root.child("groups/\(orderID)/").queryOrdered(byChild: "ownerId").queryEqual(toValue: self.currentUserId).observe(.value, with: { (snapshot: DataSnapshot!) in
print(snapshot.childrenCount, "quanti sono")
count += (Int(snapshot.childrenCount))
print(count)
})
}
}
})
with this i can get a count but it updates all cycles... i need i need the final value outside
One important aspect of Firebase Structures is denormalizing or flattening the structure. Denormalized data generally makes queries much easier and while conceptually the structure you are using works for some tasks, it makes doing the query you want challenging.
So, I would suggest an alternate structure that would make the query super simple, and not loose other functionality.
A change to the structure like this:
groups
group1: true
group2: true
subgroups
subgroup1(autoid)
id
ownerId
description
belongs_to_group: "group1"
subgroup2(autoid)
id
ownerId
description
belongs_to_group: "group2"
Then if you want to count all of subgroups with a particular ownerId
let subGroupsRef = self.ref.child("subgroups")
let query = subGroupsRef.queryOrdered(byChild: "ownerId").queryEqual(toValue: "their id")
query.observeSingleEvent(of: .value) { snapshot in
let count = snapshot.childrenCount
print(count)
}
Edit:
Based on the comment, here's an way to get the count based on your current structure. It's pretty brute force and the code could be reduced considerably but I left it verbose for readability
let groupsRef = self.ref.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
var count = 0
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
for subGroupChild in groupSnap.children {
let subGroupSnap = subGroupChild as! DataSnapshot
let dict = subGroupSnap.value as! [String: Any]
let uid = dict["owner_id"] as! String
if uid == "uid_0" {
count += 1
print(count)
}
}
}
print("total found \(count)")
})
Where this fails is if you have a lot of nodes as they are all initially loaded in (by .value) so it could be iterated over in code. If it's a few thousand it works well and is very fast.
You can achieve this step by doing a single observe of .childAdded type. The .childAdded on an sub child, in your example groups, is like a for loop that iterate all nodes.
With this configuration you can append a .queryOrdered(byChild:) and a .queryEqual(toValue:):
ref.child("groups")
.queryOrdered(byChild: "ownerID")
.queryEqual(toValue: 123)
.observe(.childAdded)
{ (snap) in
print(snap)
}
After that, if you want to count all this child, you need to add a property on your class
This is a test example:
To optimize performance remember to add a .indexOn rule on your firebase app:
"groups" : {
".indexOn" : "ownerID"
}
Hope this help you ;)

Swift Firebase query child of child [duplicate]

This question already has answers here:
Firebase Query Double Nested
(3 answers)
Closed 5 years ago.
So I'm stuck for half a day here, I'm trying to get all the subjects of Arya but I'm having a hard time doing it.
Here's the sample structure in Firebase.
-Subjects
-math
id: 1
name: Math
-students
-Arya
id: 1
name: Arya
-JonSnow
id: 2
name: JonSnow
+justsomename
+science
+english
+history
+computer
Then I found this one Querying in Firebase by child of child.
I tried this
ref.queryOrdered(byChild: "students/name").queryEqual(toValue: "Arya").observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
ref == Subjects
but this is the return
Snap (subjects) <null>
Is the query correct and I'm just doing something wrong?
Maybe something like this.... could work...
var subjects = [String]()
ref.observeSingleEvent(of: .value, with: { snapshot in
let value = snapshot.value as? NSDictionary
let postsIds = value?.allKeys as! [String] // this will put all the subjects such as math into an array called postIDs
for postId in postsIds { //this is going to cycle through the array and check each one for your student
let refToPost = Database.database().reference(withPath: "Subjects" + postId)
refToPost.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("Arya") {
self.subjects.append(snapshot)
}
})
}
})
print("this is the ist of subjects with my student: \(subjects)")

Retrieving an array from Firebase [duplicate]

This question already has answers here:
How to retrieve objects from firebase by key value
(2 answers)
Closed 6 years ago.
I have an array in Firebase composed of 1's and 0's (true and false basically), they are all stored as separate key/value pairs. I would like to retrieve them from Firebase and append each value to an array in Swift.
I have tried this (found elsewhere on SO) but it is not working.
let ref = Firebase(url:"https://<<unique>>.firebaseio.com/users/\(self.UUID)/scoreArray")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
print("snap is null")
} else {
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {
self.scoreArray.append(rest.value! as! String)
}
}
})
It doesn't crash, it just doesn't fill the array, even though if I print(rest.value) it will give me the array.
So I guess the question is, how do I convert rest.value into a usable form?
EDIT Firebase structure as requested.
66EC8AC4-<<rest of UUID>>
creation_date: "Jun 10, 2016"
extra_quiz_1
q1: "a"
q10: "b"
<<Continues>>
scoreArray
0: "0"
1: "1"
2: "0"
3: "0"
<<continues>>
Working with Array's in Firebase is challenging and in general there are better options to structure data.
In this use case, it may work so here's how it's done.
Given a structure similar to yours:
quiz_0
quiz_name: The Planets
scores
0: Mercury
1: Venus
2: Earth
here's how you would read in the data
let quizzesRef = self.myRootRef.childByAppendingPath("quizzes")
quizzesRef.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
let quiz_name = child.value["quiz_name"] as! String
print(quiz_name)
let scores = child.value["scores"] as! NSArray
print(scores) //scores is an array
let answer0 = scores[0] //just to demonstrate accessing the array
print(answer0)
}
})
and the output
Planets
(
Mercury,
Venus,
Earth
)
Mercury
That being said, don't use arrays. Here's a suggestion that may be a far more flexible. Renumbering questions is a snap, modifying the question or answer is easy as well. The -Asidijaid keys are generated using childByAutoId - helps to disassociate dynamic data from static keys.
quiz_0
quiz_name: The planets
quiz_data
-Asok99idkasdsl
question_number: 0
question: Which planet closet to the Sun?
answer: Mercury
-Yklaosokjdinoisd
question_number: 1
question: Which rocky planet is hotter than Mercury
answer: Venus
-Klkooksow999sdd
question_number: 2
question: What is the third planet from the Sun
answer: Earth

Firebase/Swift queryOrder then match value

I asked a question yesterday that was marked as a duplicate, and when I updated the question it was not unmarked. So I am asking again here (as per stackoverflow's recommendation).
I am trying to sort by multiple values in firebase. I understand that is not possible, but i was given an example in another language which is only half helpful as how to go about doing it the right way. In any case i tried to follow the example given here Query based on multiple where clauses in firebase .
This is the structure of my firebase
room
-KJe22sduQMz1DIs_DH6
allowedParticipants:
14
createdBy:
"Mr Tester"
members:
"nmeMYnnSatRch5qKPJKIe7jEOLy2"
participating:
true
status:
"seedling"
theme:
"Cats"
totalNumberOfMembers:
1
and this is the code that I am trying to get to work
ref.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
let themeOfEvent = snapshot.value
if themeOfEvent?.value == pickedTheme {
print("foo")
}
}
Could somebody please post a useful comment or answer to help me?
Thank you
I was able to get help
This works
func listOfPossibleCompetitionsFromFirebase(){
let createdRoomRef = firebase.child("room")
createdRoomRef.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
var themeCount = 0
self.listOfOpenComps.removeAll()
if let tmp = snapshot.value as? [String:AnyObject] {
let keys = tmp.keys
for key in keys {
if let roomDetails = (tmp[key] as? [String:AnyObject]) {
if let themeOfEvent = roomDetails["theme"] as? String where themeOfEvent == pickedTheme {
themeCount += 1
self.listOfOpenComps.append(key)
}
}
}
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
print("rooms count: \(themeCount)")
}
}