Ordering with Arrays

Using Y.Array to maintain element order in collaborative applications.

Overview

Y.Array provides ordered sequences that handle concurrent insertions gracefully. Combined with ID-based storage, Y.Array becomes the standard way to represent ordered collections.

The Pattern

Store only IDs in Y.Array; store content in Y.Map. See ID-Based Storage for the complete pattern and core operations.

Concurrent Insert Behavior

When two users insert at the same position simultaneously:

Initial: [A, B, C]

User 1 inserts X after A: [A, X, B, C]
User 2 inserts Y after A: [A, Y, B, C]

Merged: [A, X, Y, B, C] or [A, Y, X, B, C]

The order of X and Y is determined by client IDs (arbitrary but consistent). Both items appear—neither is lost.

Grouped Ordering

For items grouped into categories, use a Map of Arrays:

items/           → Map<itemId, ItemData>
groupOrders/     → Map<groupId, Y.Array<itemId>>
  ├── 'todo'     → ['k8d2fn3m', 'j7n3ks8w']
  ├── 'doing'    → ['m4x9pt2q']
  └── 'done'     → ['p2r6vm4c', 'q5t8wn2x']

Moving an item between groups: delete ID from source array, insert into target array, update item’s groupId field—all in one transaction.

Performance Tips

Large arrays (10,000+ items):

  • Cache order.toArray() instead of calling repeatedly
  • Use UI pagination/virtualization

Batch operations:

// WRONG: multiple syncs
for (const id of idsToRemove) {
  order.delete(order.toArray().indexOf(id), 1)
}

// CORRECT: single transaction, delete from end first
yDoc.transact(() => {
  const indices = idsToRemove
    .map(id => order.toArray().indexOf(id))
    .filter(i => i !== -1)
    .sort((a, b) => b - a)  // Descending
  for (const i of indices) order.delete(i, 1)
})

See Also