Firebase looping multi level nodes - ios

I am trying to get results from a node that has one more level of nodes and cannot get through it:
I am retrieving the data like this (it works perfectly but not for the node called "items"):
func fetchMeals() {
print("start pulling data")
let user = Auth.auth().currentUser?.uid
ref.child("Users_Food_Data").child(user!).queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
if snapshot.value as? [String : AnyObject] != nil {
let allMeals = snapshot.value as! [String : AnyObject]
self.foodArray.removeAll()
for (_, value) in allMeals {
let foodToShow = FoodStruct()
if let calories = value["calories"] as? String,
let itemCarbs = value["energy"] as? String,
let itemProteins = value["proteins"] as? String,
let itemFat = value["fat"] as? String,
let date = value["date"] as? String,
let id = value["id"] as? String,
let name = value["name"] as? String,
let interval = value["interval"] as? Int {
foodToShow.itemKcal = calories
foodToShow.itemCarbs = itemCarbs
foodToShow.itemProteins = itemProteins
foodToShow.itemFat = itemFat
foodToShow.id = id
foodToShow.interval = interval
foodToShow.date = date
foodToShow.itemName = name
self.foodArray.append(foodToShow)
self.collectionView?.reloadData()
}
self.breakfastArray.sort(by: {$0.interval! > $1.interval!})
}
}
})
}
And the database looks like this:
"Users_Food_Data" : {
"JztkBihGgda0jtSpe6pNwt8hZu13" : {
"Breakfast:23 Sep 2017" : {
"calories" : "1145.0",
"date" : "23 Sep 2017",
"energy" : "238.8",
"fat" : "3.0",
"id" : "-Kukx_9lSpCh3lcEMzap",
"interval" : 1.506207565807117E9,
"items" : {
"-KukxKAntXDaS__v3ZLA" : {
"calories" : "30",
"date" : "23 Sep 2017",
"energy" : "6",
"fat" : "0.1",
"interval" : 1.506207500336909E9,
"itemKey" : "-KukxKAntXDaS__v3ZLA",
"mealKey" : "-KukxKAntXDaS__v3ZLB",
"name" : "Strawberries",
"proteins" : "0.8",
"quantity" : "3"
},
"-KukxLYmyg32lU1D3Wh3" : {
"calories" : "29",
"date" : "23 Sep 2017",
"energy" : "9",
"fat" : "0.5",
"interval" : 1.506207505968336E9,
"itemKey" : "-KukxLYmyg32lU1D3Wh3",
"mealKey" : "-KukxLYmyg32lU1D3Wh4",
"name" : "Lemon",
"proteins" : "1.1",
"quantity" : "1"
}
},
"name" : "Breakfast",
"proteins" : "17.0"
},
"Breakfast:24 Sep 2017" : {
"calories" : "959.0",
"date" : "24 Sep 2017",
"energy" : "106.4",
"fat" : "46.1",
"id" : "-KunWOZeSxW9eCIA6O1z",
"interval" : 1.506250519537633E9,
"items" : {
"-KulrJq6jOpsG6oiJuDM" : {
"calories" : "458",
"date" : "24 Sep 2017",
"energy" : "4.6",
"fat" : "45",
"interval" : 1.506222704055992E9,
"itemKey" : "-KulrJq6jOpsG6oiJuDM",
"mealKey" : "-KulrJq6jOpsG6oiJuDN",
"name" : "Coconut",
"proteins" : "4",
"quantity" : "1"
},
How do I do that when I know only "Users_Food_Data" and user ID. I just want to list them in the apropiate cells.

Here is my suggestion.
First looking on your database structure, you currently have something like this :
Users_Food_Data
|_userid
|_FoodStruct
|_items
|_id_1
|_data like calories, date...
|_id_2
|_data...
There is too much nodes and this kind of hierarchy doesn't respect what we call Denormalization. Remember that Firebase database is like a NoSQL database and it's a big difference compared to SQL classic databases. Here is an explanation to the best practice to denormalize your database structure : Structure your database
What i can suggest is structure your database more like this :
One node :
UsersFood
|_userid
|_breakfeast_id_1
|_breakfeast_id_2...
2nd node
Breakfeasts
|_breakfeast_id_1
|_item_id_1
|_item_id_2...
3rd node
Items
|_item_id_1
|_calories
|_date
|_energy...
Then in your code, you can :
Observe with a single event on UsersFood node all the breakfeast ids
Then with all breakfeast ids get items_id
Finally get items_id data
Hope it will help you through your code.
Edit 1 : You can also loop through a child node by using this :
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
//DO WHAT YOU NEED TO DO
}

Related

how to filter swiftyjson array

I'll try every solution which I get from stack overflow but its my bad luck. I store my JSON response in [Any] array like this:
var json = JSON()
var arrClientType = [Any]()
self.json = JSON(value) //value is json data
self.arrClientType = self.json["client_type_data"].arrayValue
now, I want to filter this array and reload that filtered data in tableview.
[{
"client_type_name" : "Asset Manager",
"client_type_id" : 1
}, {
"client_type_name" : "Broker Dealer",
"client_type_id" : 5
}, {
"client_type_name" : "Corporate",
"client_type_id" : 8
}, {
"client_type_name" : "Custodian and Prime Broker",
"client_type_id" : 3
}, {
"client_type_name" : "Diversified Financial Services Firms",
"client_type_id" : 4
}, {
"client_type_name" : "Fund Administrator",
"client_type_id" : 6
}, {
"client_type_name" : "Hedge Fund Manager",
"client_type_id" : 2
}, {
"client_type_name" : "Individual",
"client_type_id" : 7
}]
I'll try this also :
let filtered = JSON(self.arrList).arrayValue.filter({
$0["client_type_name"].arrayValue.map({ $0.stringValue }).contains("Broker Dealer")
})
print ("filterdData: \(filtered)")
but it give me entry filter array.
Please help me.
try this
filtered = arrList.filter { $0["client_type_name"].stringValue.contains("Broker Dealer") }
and change your self.arrlist to array of dictiony type
var arrList: [JSON] = []
var filtered :[JSON] = []
Try this one , you will be able to filtered the data which contain the "client_type_name" as a Broker Dealer.
let arrClientType = [["client_type_name":"Asset Manager" , "client_type_id":1] , ["client_type_name":"Broker Dealer" , "client_type_id":5] , ["client_type_name":"Corporate" , "client_type_id":8]]
if let filteredData = arrClientType.filter({(($0 as? [String:Any])?["client_type_name"] as? String ?? "") == "Broker Dealer"}) as? [Any] {
print(filteredData)
}
The filteredData is the arrayOfDictionary which contain the dictionaries in which "client_type_name" as a Broker Dealer.

Adding to dictionary dynamically

I 'm having an array of dictionary like so...
[
{
"id" : "3",
"sellingPrice" : "520",
"quantity" : "15"
},
{
"id" : "5",
"sellingPrice" : "499",
"quantity" : "-1"
},
{
"id" : "8",
"sellingPrice" : "500",
"quantity" : "79"
}
]
Now I want to add to the dictionary another key called remaining_balance with a value of 420,499 & 500 respectively. How can I achieve this..? Hope somebody can help...
It seems like you want to add a value to your dictionary that is an array:
var arrDict = Array<Dictionary<String,Any>>() //Your array
arrDict.append(["id":"3","sellingPrice":"520","quantity":"13"])
arrDict.append(["id":"5","sellingPrice":"43","quantity":"32"])
arrDict.append(["id":"8","sellingPrice":"43","quantity":"33"])
let arrValue = ["420","499","500"] //Your remaining value in array
print("Before ",arrDict)
for (index,dict) in arrDict.enumerated() {
var dictCopy = dict //assign to var variable
dictCopy["remaining_balance"] = arrValue[index]
arrDict[index] = dictCopy //Replace at index with new dictionary
}
print("After ",arrDict)
EDIT
If you are able keep an index of an array it would be possible,
Assuming that you have the index of an array
var dictCopy = arrDict[index]
dictCopy["remaining_balance"] = "666" //Your calculated value
arrDict[index] = dictCopy //Replace at index with new dictionary
var newKV = [["remaining_balance": "420"],["remaining_balance": "490"],["remaining_balance": "500"]]
let array = [["id":"3", "sellingPrice":"520", "quantity":"15"], ["id":"5", "sellingPrice":"520", "quantity":"15"], ["id":"8", "sellingPrice":"520", "quantity":"15"]]
let newArray = array.enumerated().map { (index : Int, value: [String: String]) -> [String: String] in
var dic = value
dic.merge(newKV[index]) { (_, new) -> String in
new
}
return dic
}
You could achieve it by mapping your array:
var myArray = [["id": "3", "sellingPrice": "520", "quantity" : "15"], ["id": "5", "sellingPrice": "499", "quantity" : "-1"], ["id": "8", "sellingPrice": "500", "quantity" : "79"]]
print(myArray)
/*
[["id": "3", "sellingPrice": "520", "quantity": "15"],
["id": "5", "sellingPrice": "499", "quantity": "-1"],
["id": "8", "sellingPrice": "500", "quantity": "79"]]
*/
print("___________________")
var remainingBalanceDesriedValue = 420
myArray = myArray.map { (dict: [String: String]) -> [String: String] in
var copyDict = dict
copyDict["remaining_balance"] = "\(remainingBalanceDesriedValue)"
remainingBalanceDesriedValue = (remainingBalanceDesriedValue == 420) ? 499 : (remainingBalanceDesriedValue == 499) ? 500 : 420
return copyDict
}
print(myArray)
/*
[["sellingPrice": "520", "quantity": "15", "id": "3", "remaining_balance": "420"],
["sellingPrice": "499", "quantity": "-1", "id": "5", "remaining_balance": "499"],
["sellingPrice": "500", "quantity": "79", "id": "8", "remaining_balance": "500"]]
*/
Let's assume you have an array of dictionaries like so:
var arrayOfDictionaries = [
[
"id": 3,
"sellingPrice": 520,
"quantity": 15
]
]
It is important that arrayOfDictionaries is not a let constant, otherwise it is considered immutable and you can not call append on it.
Now you init a new dictionary like:
let newDictionary = [
"id": 10,
"remaining_balance": 420,
"quantity": 15
]
Now add the newDictionary like
arrayOfDictionaries.append(newDictionary)
If the order is important
If the order is important there are a couple of ways to go about that.
When calling append the new value (in this case the new dictionary) will always be inserted at the bottom of the array.
If for some reason you can not call append in the correct order you could use insert, which inserts your dictionary at a specific position.
Yet another way is to append the values wildly and after you are done, call sort on the array.
Improvement Tips
Notice that for the values I did not use strings, as you only have numbers like "id" : 30.
Also, if you want the second key to be called remaining_balance you should call the first key selling_price instead of sellingPrice. Because of conistency.
Alternative approach
As far as I have understood you are trying to implement some software that is responsibly for selling some products.
I think you are tackling this problem from a completely wrong side.
I think you should read about database relationships. Selling products actually is a very common problem.
Maybe this will help you. I would offer a possible solution myself, but I think this misses the point of your question.
If you decide to use the database approach, you won't necessarily have to use a database. You can take the approach and implement it using simple structs/classes/arrays.
I noticed this question lacks an extension answer, yes.. I'm gonna be that guy, so here it is. This could be made more generic by supporting other types of dictionaries, feel free to pitch in ;)
Inspiration from #eason's answer.
var newKV = [["remaining_balance": "420"],["remaining_balance": "490"],["remaining_balance": "500"]]
var array = [["id":"3", "sellingPrice":"520", "quantity":"15"], ["id":"5", "sellingPrice":"520", "quantity":"15"], ["id":"8", "sellingPrice":"520", "quantity":"15"]]
extension Array where Element == [String: String] {
enum UniquingKeysStrategy {
case old
case new
}
mutating func merge(with target: Array<Element>, uniquingKeysWith: UniquingKeysStrategy = .new) {
self = self.merged(with: target)
}
func merged(with target: Array<Element>, uniquingKeysWith strategy: UniquingKeysStrategy = .new) -> Array<Element> {
let base = self.count > target.count ? self : target
let data = self.count > target.count ? target : self
return data.enumerated().reduce(into: base, {
result, data in
result[data.offset]
.merge(data.element, uniquingKeysWith: {
old, new in
if strategy == .new { return new }
return old
})
})
}
}
let mergedArrays = newKV.merged(with: array, uniquingKeysWith: .old)
array.merge(with: newKV)
Happy Coding :)

Apply filter and sorting same time firebase [duplicate]

This question already has answers here:
Query based on multiple where clauses in Firebase
(8 answers)
Closed 6 years ago.
My firebase json tree is like
{
"-KZVOY6qU29fJlRSwRNo" : {
"fedStateDetails" : "Fasted",
"targetkcal" : 596,
"timestamp" : 1.482307285673975E9,
"userid" : "816",
"workout_id" : "123"
},
"-KZVSmeW94XetROs0nsN" : {
"fedStateDetails" : "Fasted",
"targetkcal" : 1874,
"timestamp" : 1.482308270356936E9,
"userid" : "47",
"workout_id" : "129"
},
"-KZWGdx-j1q7J37x0wck" : {
"fedStateDetails" : "Fasted",
"targetkcal" : 0,
"timestamp" : 1.482321866382767E9,
"userid" : "47",
"workout_id" : "40"
}
}
So all I need is to get those users where "userid" is 47 and "timestamp" is between given range, My code looks like below.
let startTime = Date().timeIntervalSince1970 - 10800.0
let endTime = Date().timeIntervalSince1970
self.dbRef.child(FireBaseTable.workOutInfoTable)
.queryOrdered(byChild: "timestamp")
.queryStarting(atValue: startTime, childKey: "timestamp")
.queryEnding(atValue: endTime, childKey: "timestamp")
.queryEqual(toValue: "47")
.observe(.value) { (snapshot1 :FIRDataSnapshot?) in
if let value : Dictionary<String,AnyObject> = snapshot1?.value as? Dictionary<String,AnyObject>
{
print("*********")
print(value)
}
}
But I can't apply two order by together. Please help.
Thanks in advance.
If the majority of the times you need to access these items, it'll be per user, you can change your data model:
{
"user816": {
"-KZVOY6qU29fJlRSwRNo" : {
"fedStateDetails" : "Fasted",
"targetkcal" : 596,
"timestamp" : 1.482307285673975E9,
"workout_id" : "123"
}
},
"user47": {
"-KZVSmeW94XetROs0nsN" : {
"fedStateDetails" : "Fasted",
"targetkcal" : 1874,
"timestamp" : 1.482308270356936E9,
"workout_id" : "129"
},
"-KZWGdx-j1q7J37x0wck" : {
"fedStateDetails" : "Fasted",
"targetkcal" : 0,
"timestamp" : 1.482321866382767E9,
"workout_id" : "40"
}
}
}
Now you can access the data for a specific user with child() and then use queryOrder(byChild:"timestamp") to order them by timestamp.
I recommend:
reading NoSQL data modeling
viewing Firebase for SQL developers
studying my answer Query based on multiple where clauses in firebase

how to display mulitple dictionary or array data in single tableview cell in swift ios

I am new to iOS development can any one give me some idea in swift. I have JSON data after parsing i am storing data in array like example.
"orderId" : 146,
"total" : 2,
"created_at" : "2016-09-19 11:08:51",
"categories" : [
{
"name" : "Bleach",
"qrCode" : "SRY-001-0098",
"weight" : 1,
"categoryId" : "57dbd9ca8e1a0919c9075d13",
"amount" : 1
},
{
"name" : "Normal (26+ lbs)",
"qrCode" : "SRY-001-0099",
"weight" : 1,
"categoryId" : "57dbd9708e1a094ecb10cfb1",
"amount" : 1
}
],
"completed_at" : "2016-09-19 00:00:00"
I have four arrays :
orderarray
totalarray
createatarrays
cartegoriesarray
completedarray
orderarray[0] = 146
totalarray[0] = 2
createatarrays[0] = "2016-09-19 11:08:51"
cartegoriesarray[0] =
[
{
"name" : "Bleach",
"qrCode" : "SRY-001-0098",
"weight" : 1,
"categoryId" : "57dbd9ca8e1a0919c9075d13",
"amount" : 1
},
{
"name" : "Normal",
"qrCode" : "SRY-001-0099",
"weight" : 1,
"categoryId" : "57dbd9708e1a094ecb10cfb1",
"amount" : 1
}
]
completedarray[0] = "2016-09-19 00:00:00"
Using indexpathrow i can able to display orderarray, totalarray, createdarray based on index path in tableview cell.
But here categoryarray how can i display inside array again two dictionary is there ?
I want to display name" : "Bleach", "name" : "Normal" in single tableview cell based on index path like categoryarray[0]
How to get these two names in single string plz give me some idea..
Thanks.
You need to loop the catergoryarray and get each dictionary out. Then you can get the individual values out of the dictionary.
var name = "";
//Loop the array of dictionaries
for dict in categoryarray as! NSDictionary {
//extract the items from the dictionary. and concatenate names
name = name + " " + dict["name"];
}
print("Name: ", name)
You can then use that information to populate your table cells as you wish. Obviously you will want to modify how the names are concatenated to suit your needs.
Hope that helps.

How to retrieve multiple data in one query Firebase

I'm new in firebase and nosql. I followed the guide for structuring data and here is what my data look like
{"Guardians" : {
"jojo-pti-gros" : {
"-KBT2ui6wlC6Fgk1c7Xa" : {
"creationDate" : "2016-02-26 15:50:56",
"level" : "2",
"light" : "2",
"type" : "Titan"
}
}
},
"Players" : {
"jojo-pti-gros" : {
"console" : "PS4",
"creationDate" : "2016-02-26 15:50:39",
"currentGuardian" : "-KBT2ui6wlC6Fgk1c7Xa",
"email" : "toto#gmail.com",
"gamertag" : "jojo-pti-gros",
"guardians" : {
"-KBT2ui6wlC6Fgk1c7Xa" : "true"
}
}
},
"Teams" : {
"-KBhfH9U3CxQPZiCs5ru" : {
"activity" : "Raid - Normal - Crota's end",
"creationDate" : "2016-02-29 16:37:14",
"language" : "Any language",
"leader" : "jojo-pti-gros",
"maxPlayers" : "6",
"mic" : "true",
"minimumLight" : "55",
"players" : {
"jojo-pti-gros" : "true"
},
"teamDescription" : "Chaud pour un raid"
}
}}
When I try to retrieve a team I would like to have a json like this.
"-KBhfH9U3CxQPZiCs5ru" = {
activity = "Raid - Normal - Crota's end",
creationDate = "2016-02-29 16:37:14",
language = "Any language",
leader = "jojo-pti-gros",
maxPlayers = 6,
mic = true,
minimumLight = 55,
players = {
"jojo-pti-gros" = {
"creationDate" = "2016-02-26 15:50:56",
"level" = "2",
"light" = "2",
"type" = "Titan"
}
},
teamDescription = "Chaud pour un raid"
}
But I have this json instead
"-KBhfH9U3CxQPZiCs5ru" = {
activity = "Raid - Normal - Crota's end",
creationDate = "2016-02-29 16:37:14",
language = "Any language",
leader = "jojo-pti-gros",
maxPlayers = 6,
mic = true,
minimumLight = 55,
players = {
"jojo-pti-gros" = true
},
teamDescription = "Chaud pour un raid"
}
My code right now
let fb = Firebase(url:myUrl)
fb.childByAppendingPath("/Teams").observeSingleEventOfType(.Value, withBlock: { teams in
var teamsArray: [Team] = []
if (teams.value is NSNull) {
print("no teams found")
} else {
print(teams.value)
for team in teams.value as! NSDictionary {
let newTeam = Team()
newTeam.dictionaryToTeam(team.value as! NSDictionary)
newTeam.id = String(team.key)
for player in newTeam.playerDictionary as NSDictionary {
GuardianAPI.sharedInstance.getGuardianByGamertagAndID(player.key as! String, id: player.value as! String, handler: {newGuardian in
newTeam.playerDictionary.setValue(newGuardian, forKey: player.key as! String)
})
}
teamsArray.append(newTeam)
}
handler(true)
}
})
I don't know how to make a query with juncture like SQL.
Can somebody know how to resolve this ?
There are a couple of directions to go with this.
One option is to read in your team node, separate out the player refs into perhaps an array and then iterate over the array to get each player ref and load each player with a separate Firebase call
A better option is to store a reference back to the team in each player's node
"Players" : {
"jojo-pti-gros" : {
team: "-KBhfH9U3CxQPZiCs5ru"
}
Observe the teams node to load in the team data then then query the players node for all players that have a child: team = "-KBhfH9U3CxQPZiCs5ru"
Firebase *playersRef = [myRootRef childByAppendingPath:#"Players"];
FQuery *query1 = [playersRef queryOrderedByChild:#"team"];
FQuery *query2 = [query1 queryEqualToValue:#"-KBhfH9U3CxQPZiCs5ru"];
[query2 observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"key: %# %#", snapshot.key, snapshot.value);
}];
The result will be a snapshot filled with all of the players that belong to the team.

Resources