///|
fn has_index_aliases(fields : Map[String, Value]) -> Bool {
  let indices : Map[Int, Bool] = Map([])
  for key, _ in fields {
    if key.is_empty() {
      continue
    }
    let first = key[0].to_int()
    if !((first >= 48 && first <= 57) ||
      first == 43 ||
      first == 45 ||
      (first >= 128 && numeric_digit(first) >= 0)) {
      continue
    }
    let index = numeric_index(key)
    if index >= 0 {
      if indices.contains(index) {
        return true
      }
      indices[index] = true
    }
  }
  false
}

///|
// Numeric aliases in object-to-list conversion overwrite in the reference
// object's hash-bucket traversal order. Keep insertion order inside a bucket.
fn object_iteration_keys(fields : Map[String, Value]) -> Array[String] {
  let mut capacity = 16
  while fields.length() > capacity * 3 / 4 {
    capacity *= 2
  }
  let keyed : Array[(Int, Int, String)] = []
  for position, key in fields.keys().collect() {
    let mut hash = 0U
    for i = 0; i < key.length(); i = i + 1 {
      hash = hash * 31U + key[i].to_int().reinterpret_as_uint()
    }
    let bucket = (hash ^ (hash >> 16)).reinterpret_as_int() & (capacity - 1)
    keyed.push((bucket, position, key))
  }
  keyed.sort_by((a, b) => {
    if a.0 == b.0 {
      a.1.compare(b.1)
    } else {
      a.0.compare(b.0)
    }
  })
  keyed.map(item => item.2)
}