---
title: "Handling Synchronized Arrays with Real-Time Firebase Data"
description: "Handling Synchronized Arrays with Real-Time Firebase Data"
published: "2014-05-06"
last_updated: "2014-05-06"
authors:
  - "Kato Richardson (Support Hacker)"
categories:
  - "Realtime Database"
  - "Best Practices"
canonical_url: "https://firebase.blog/posts/2014/05/handling-synchronized-arrays-with-real"
---

# Handling Synchronized Arrays with Real-Time Firebase Data

*Published May 6, 2014 by Kato Richardson (Support Hacker)*

---
_In our last blog post, we discussed [best practices for using arrays in Firebase](https://firebase.blog/posts/2014/04/best-practices-arrays-in-firebase). If you haven't read that, please start there. This blog post will discuss advanced concepts for converting between real-time objects and sortable, filterable arrays._

Okay, so let's get some working gloves on and start handling synchronized arrays. If we want client-side sorting, filtering, and other things arrays are great at, but also need the awesome collaborative aspects of real-time objects, then we're going to be up to our elbows in code. We're going to work in JavaScript here, because that's what I'm most familiar with. All of these techniques can be translated to iOS and Android as well.

### Using Existing Libraries
Before we get started, it's worth mentioning that there are some great frameworks that have already resolved the problems we face with synchronized arrays. In fact, we'll be drawing from the principles of their array management throughout this article.

[EmberFire](https://github.com/firebaseextended/emberFire) provides array handling out of the box, integrating Firebase neatly into the [Ember.js](https://www.emberjs.com) framework. If you're already a glowing Ember dev, check out the [Ember + Firebase Guide](https://www.firebase.com/quickstart/emberjs.html) for details.

[AngularFire](https://github.com/firebaseextended/angularFire) version 0.8.0 provides utilities for using arrays in [Angular](http://www.angularjs.org). If you are an ngDev, Check out the [Angular + Firebase Guide](https://www.firebase.com/docs/web/bindings/angular/quickstart.html) for details.

[BackboneFire](http://github.com/firebaseextended/backbonefire) provides similar capabilities for those osteologists who prefer [Backbone](http://backbonejs.org/)'s flexible MVVM.

### Best Practices for Synchronized Arrays
Drawing from the lessons learned in developing libraries like AngularFire, we can lay down a solid set of principles to keep ourselves out of trouble:

*   Make the synchronized array read only (use setter methods instead of splice/pop/etc)
*   Add a unique id (the Firebase key) to each record
*   Server changes get written directly into our array
*   Local changes are pushed to the server, then trickle back

In other words, our array is essentially a one-directional loop. Changes come from the server into the array, we read them out, we push our local edits to the server, they trickle back into the array.

Okay, let's get started handling synchronized arrays in Firebase.

### Pulling Data from Firebase
Let's start simple:

```js
function getSynchronizedArray(firebaseRef) {
  var list = [];
  // put some magic here
  return list;
}
```

This provides a method we can pass a firebaseRef into and get back an array. Now let's put some data into it.

```js
function getSynchronizedArray(firebaseRef) {
  var list = [];
  syncChanges(list, firebaseRef);
  return list;
}

function syncChanges(list, ref) {
  ref.on('child_added', function _add(snap, prevChild) {
    var data = snap.val();
    data.$id = snap.key(); // assumes data is always an object
    var pos = positionAfter(list, prevChild);
    list.splice(pos, 0, data);
  });
}

// similar to indexOf, but uses id to find element
function positionFor(list, key) {
  for(var i = 0, len = list.length; i < len; i++) {
    if( list[i].$id === key ) {
      return i;
    }
  }
  return -1;
}

// using the Firebase API's prevChild behavior, we
// place each element in the list after it's prev
// sibling or, if prevChild is null, at the beginning
function positionAfter(list, prevChild) {
  if( prevChild === null ) {
    return 0;
  }
  else {
    var i = positionFor(list, prevChild);
    if( i === -1 ) {
      return list.length;
    }
    else {
      return i+1;
    }
  }
}
```

Okay, now our array is getting populated from Firebase. Next, let's deal with the other CRUD operations, and move events.

```js
function syncChanges(list, ref) {
  ref.on('child_added', ...); // example above

  ref.on('child_removed', function _remove(snap) {
    var i = positionFor(list, snap.key());
    if( i > -1 ) {
      list.splice(i, 1);
    }
  });

  ref.on('child_changed', function _change(snap) {
    var i = positionFor(list, snap.key());
    if( i > -1 ) {
      list[i] = snap.val();
      list[i].$id = snap.key(); // assumes data is always an object
    }
  });

  ref.on('child_moved', function _move(snap, prevChild) {
    var curPos = positionFor(list, snap.key());
    if( curPos > -1 ) {
      var data = list.splice(curPos, 1)[0];
      var newPos = positionAfter(list, prevChild);
      list.splice(newPos, 0, data);
    }
  });
}
```

Great, now our array is completely synchronized with the remote data. Now we just need some local editing capabilities.

### Pushing Local Updates
We won't modify our array directly. We can do ops like `sort` to re-order the data, but if we start trying to  
do ops like `splice` or `pop`, and have the server modifying this array as well, things will get ugly.

Instead, we'll directly push changes to the server with some quick wrapper methods. Since Firebase triggers events  
for local changes immediately, without waiting for a server response, this is quite performant and simplifies the  
process as well:

```js
function getSynchronizedArray(firebaseRef) {
  var list = [];
  syncChanges(list, firebaseRef);
  wrapLocalCrudOps(list, firebaseRef);
  return list;
}

function syncChanges() { /* in examples above */ }

function wrapLocalCrudOps(list, firebaseRef) {
   // we can hack directly on the array to provide some convenience methods
   list.$add = function(data) {
      return firebaseRef.push(data);
   };

   list.$remove = function(key) {
     firebaseRef.child(key).remove();
   };

   list.$set = function(key, newData) {
     // make sure we don't accidentally push our $id prop
     if( newData.hasOwnProperty('$id') ) { delete newData.$id; }
     firebaseRef.child(key).set(newData);
   };

   list.$indexOf = function(key) {
     return positionFor(list, key); // positionFor in examples above
   }
}
```

And there we are! We can now manipulate our array by using operations like this:

```js
var ref = new Firebase(URL);
var list = getSynchronizedArray(ref);

// add a record
var newRecordId = list.$add({ foo: 'bar' }).key();

// remove a record
list.$remove(recordKey);

// update a record
var data = list[5];
data.foo = 'baz';
list.$set( data.$id, data );
```

### A Working Example
That's pretty much it! There are a few additional edge cases that we can cover, but we've created a  
synchronized array that merges local changes with server updates and keeps all of it in a tidy array.

[Here's an example project utilizing these principles](https://github.com/katowulf/Firebase.getAsArray) you can use  
as a template for baking your own solutions. Check out the README for installation instructions.

### More To Come!
At Firebase, we're never satisfied with just being awesome. We are constantly working to improve all these scenarios and have several API enhancements in the pipeline. Keep an eye on the [Google Group](https://groups.google.com/forum/#!forum/firebase-talk) for details and announcements.

Find this article useful or have additional questions? Leave a comment below or post a message on the [Google Group](https://groups.google.com/forum/#!forum/firebase-talk)!
