Because Firebase is a NoSQL JSON data store, you might have noticed that when you get some data from Firebase that it is an object. If the title wasn’t obvious enough, we are talking about using Firebase Realtime Database in a web application.
Take the following example:
firebase.database().ref('/posts').on('value', function(snapshot) {
console.log(snapshot.val());
});
Let’s imagine that we have 20 posts in our database. You’ll get back an object containing keys and objects for all of our imaginary posts.
If you’re working with a Javascript framework or library such as Aurelia, then you’ll know that iterating an object opposed to an array is more complicated (especially HTML templating).
So, here is a function I ended up writing which I use quite a lot in my Firebase applications:
function snapshotToArray(snapshot) {
var returnArr = [];
snapshot.forEach(function(childSnapshot) {
var item = childSnapshot.val();
item.key = childSnapshot.key;
returnArr.push(item);
});
return returnArr;
};
I am a big fan of verbose functions, although using the power of modern spec Javascript you can create a more condensed shorthand equivalent of the above function.
To use our newly created function
firebase.database().ref('/posts').on('value', function(snapshot) {
console.log(snapshotToArray(snapshot));
});
If you are using a transpiler like Babel, writing using TypeScript or targeting evergreen browsers like Chrome and Firefox, a nicer solution is:
const snapshotToArray = snapshot => Object.entries(snapshot).map(e => Object.assign(e[1], { key: e[0] }));
You call our shorthand function the same, but it’s a less verbose and harder to understand one-line function. Special thank you to the commenters below who have proposed their own solutions as well.
An ES7 one-liner:
let arr = Object.entries(snapshot).map(e => Object.assign(e[1], { key: e[0] }));
Allen,
That is really nice. Thanks for sharing that!
Thanks, very useful pattern !
Very Thanks.
That key interrupts the loop number, for example if a node only contain 2 child then another extra child ‘key’ is added. that increase the loop number.
did you try it ?
the problem in the first place is we get object of values…
so we dont have .forEach … because its unknown object and not array !
ES6:
Object.keys(s.val() || {}) .map(k => s.val()[k]);
Thank you for sharing snapshotToArray(snapshot) 🙂
Great help, thanks
Wow, thanks a lot. Dwayne & allen joslin
Hey, thanks for sharing
what do you think about this approach?
https://gist.github.com/fipo/483a07bc741e7f4b470b8bc3e6c7bdac
I prefer to keep the id and use it for the `key=` prop
I am using similar solution in angular with ionic 3. My issue is this: when I bind my datas to html, I could not get a unique key for each entry.
I am trying to implement a like/unlike button right on esch entry binded but the key for each entry is my problem.
For example: adding a delete button on each binded entry. When clicked, it deletes another entry and not the actual post.
Using navParams works fine but one has to goto post details first.
How do I get a unique key for each post binded without going to post details first? Any ideas please?
Posts are binded in timeline.html and logins in timeline.ts and post details are binded in post-details.html and navparams from post-details.ts
Object.values(snapshot.val()) also does the job
I can’t stress how helpful this was. I am finding the documentation on Firebase isn’t the most clear. Thank you for posting this solution!
hello please tell me how to fetch the resent updated value from firebase and display in map as a marker
Thank you for sharing this!
I still have no idea why they not returning an array response instead…
Thank you!
Thanks. This really helped
its working
Thanks a lot! Really helpful!
I need to be able to make an API call from one app and return an array of posts while keeping the default functionality for other apps. Have you any experience with creating a custom url to query data as an array but leaving it as is for default rest url? Would it automatically default to the url of the function?
how do you extract a specific data from an array