6

Is there any way to retrieve all the keys in a child, put them into array, and then retrieve the values for the keys and put it into another array?

Source code:

self.ref?.child("data").child("success").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
   if snapshot != nil {
        // Your answer goes here
   }
}
KENdi
  • 7,576
  • 2
  • 16
  • 31
techcoderx
  • 602
  • 1
  • 7
  • 21

4 Answers4

25

Snapshots have two properties.

snapshot.key
snapshot.value

When using an observer with .value. all of the key: value children are returned in the snapshot. They can be iterated over to capture each key: value pair like this.

ref.observeSingleEvent(of: .value, with: { (snapshot) in
    for child in snapshot.children {
        let snap = child as! DataSnapshot
        let key = snap.key
        let value = snap.value
        print("key = \(key)  value = \(value!)")
    }
})

Keep in mind that the value property could be a string, number, array or another dictionary (snapshot). In the original question it's a String.

Jay
  • 34,438
  • 18
  • 52
  • 81
6

Something like this should work (with better named variables):

  1. Cast the snapshot.value as a dictionary
  2. Use the dictionary's .keys & .values properties to retrieve them as LazyMapCollection types.
  3. Initialise an array with the collections.

Code:

self.ref?
    .child("data")
    .child("success")
    .child(userID!)
    .observeSingleEvent(of: .value, with: { (snapshot) in
        if let data = snapshot.value as? [String: Any] {
            let keys = Array(data.keys)
            let values = Array(data.values)
            ... // Your code here
        }
    }

Update - To show example where order is respected:

I would probably save the dictionary to an array, which casts it to type Array<(key: String, value: Any)>. Then, you can map it to keys or values and keep the order.

... // As above
    .observeSingleEvent(of: .value, with: { (snapshot) in
        if let data = snapshot.value as? [String: Any] {
            let dataArray = Array(data)
            let keys = dataArray.map { $0.0 }
            let values = dataArray.map { $0.1 }
            ... // Your code here
        }
    }
Yasir
  • 2,312
  • 4
  • 23
  • 35
  • The problem here is that the ordering is lost. – Jay Jul 16 '17 at 13:13
  • @Jay Updated the code to reflect your comment - does this fix your issue? – Yasir Jul 16 '17 at 14:16
  • Unfortunately that won't work if the value is another snapshot. It *will* work for this question though, as long as the OP keeps the value a string or number (i.e. a directly orderable value). I like the solution! – Jay Jul 16 '17 at 14:32
  • @Jay please if you dont mind please can you help me, check my post related to firebase observer. I don't want complete list data every time only change reflects to specfic field with observer. I am really stuck into it. link: https://stackoverflow.com/questions/58506034/access-nested-firebase-data-in-swift – Newbie Oct 23 '19 at 06:58
5

I wanted to do something similar in my project. Hope this helps and you get the idea

var photosList = [String]()
refToObserve?.observeSingleEvent(of: .value, with: {snapshot in
        if let snapshotValue = snapshot.value as? [String:Any] {
            for child in snapshot.childSnapshot(forPath: ***your path***).children {
                let myChild = child as! DataSnapshot
                if let myChildValue = myChild.value as? [String:Any] {
                    self.photosList.append(myChildValue["postId"] as! String)
                }
            }
        }
    })
Kanan
  • 142
  • 10
0

If you prefer mapping instead of iteration like me, you can do

let keys = snapshot.children.compactMap { ($0 as? DataSnapshot)?.key }
let values = snapshot.children.compactMap { ($0 as? DataSnapshot)?.value }
Lexon Li
  • 135
  • 1
  • 5