Получить следующую пару ключ-значение в объекте - программирование

Получить следующую пару ключ-значение в объекте

Учитывая ключ, я хочу найти следующее свойство в объекте. Я не могу полагаться на ключи, которые нужно заказать или последовательные (они являются uuids). Ниже вы можете увидеть тривиальный пример того, что я хочу:

var db = {
  a: 1,
  b: 2,
  c: 3
}

var next = function(db, key) {
  // ???
}

next(db, 'a');  // I want 2
next(db, 'b');  // I want 3

Мне также нужна функция prev(), но я уверен, что это будет одно и то же решение.

Это похоже на такую ​​тривиальную проблему, но я не могу для жизни понять, как это сделать.

Счастливый для решения использовать underscore.js или писать в coffeescript:)

4b9b3361

Ответ 1

Правильный ответ: вы не можете этого сделать, поскольку объекты неупорядочены в соответствии со спецификацией ECMAScript.

Я бы рекомендовал использовать упорядоченную структуру, например массив, для устранения проблемы:

var db = [
  {key: 'a', value: 1},
  {key: 'b', value: 2},
  {key: 'c', value: 3}
];

Тогда функция next может быть примерно такой:

var next = function(db, key) {
  for (var i = 0; i < db.length; i++) {
    if (db[i].key === key) {
      return db[i + 1] && db[i + 1].value;
    }
  }
};

В случае, если key не существует на db или он был последним, next возвращает undefined. если вы никогда не будете запрашивать следующий из последнего элемента, вы можете упростить эту функцию, удалив трехмерный оператор && и вернув db[i + 1].value напрямую.

Вы также можете использовать некоторые из методов утилиты Underscore.js, чтобы упростить next:

var next = function(db, key) {
  var i = _.pluck(db, 'key').indexOf(key);
  return i !== -1 && db[i + 1] && db[i + 1].value;
};

(в этом случае next может иногда возвращаться false... но он все еще является фальшивым значением:))


Теперь более прагматичный ответ может заключаться в том, что, поскольку большинство браузеров будут уважать порядок, в котором объект был инициализирован при его итерации, вы можете просто перебрать его с помощью цикла for in, как предлагают другие ответы. Я бы рекомендовал использовать Object.keys, чтобы упростить работу по итерации по массиву:

// Assuming that db is an object as defined in the question.
var next = function(db, key) {
  var keys = Object.keys(db)
    , i = keys.indexOf(key);
  return i !== -1 && keys[i + 1] && db[keys[i + 1]];
};

Ответ 2

Непосредственным решением для этого было бы хранить данные в массиве и использовать объект для простого хранения индекса в массиве, в котором существует объект.

var db = {
    data: [1, 2, 3],
    index: {
        a: 0,
        b: 1,
        c: 2
    }
};
function next(db, key) {
    var next = db.index[key] + 1;
    if (next >= db.data.length) {
        return null;
    }
    return db.data[next];
}
function prev(db, key) {
    var next = db.index[key] - 1;
    if (next < 0) {
        return null;
    }
    return db.data[next];
}
function add(db, key, value) {
    db.index[key] = db.data.push(value) - 1;
}
function remove(db, key) {
    var index = db.index[key], x, temp;
    if (index !== undefined) {
        delete db.index[key];
        db.data.splice(index, 1);
        // Update indices of any elements after the removed element
        for (x in db.index) {
            temp = db.index[x];
            if (temp > index) {
                db.index[x] = temp - 1;
            }
        }
    }
}

Основная идея заключается в использовании упорядоченной структуры, в данном случае массива, для последовательного хранения данных. В этом случае next и prev являются как постоянным временем, так и добавлением - это постоянное время амортизации, а delete - O (N).

Заказ ключей не гарантируется стандартом ECMA, поэтому for/in не нужно добавлять ключи заказа (хотя на практике это обычно является общей реализацией). В этом решении я использую массив, чтобы явно отслеживать порядок вставки.

Изменить: я упустил проблему удаления ранее с помощью сплайсинга. Индекс станет некорректным для всех значений после сращивания для удаления. Исправление не влияет на сложность выполнения операции. Более быстрая версия с меньшим количеством удалений может позволить массиву стать разреженным и вместо сращивания просто установить индекс в null, чтобы освободить любую содержащуюся там ссылку. Это снизит операцию удаления до O (1).

function remove(db, key) {
    var index = db.index[key];
    if (index !== undefined) {
        delete db.index[key];
        db.data[index] = null;
    }
}

Ответ 3

function next(db, key){   
  var found = 0; 
  for(var k in db){
    if(found){ return db[k]; }
    if(k == key){ found = 1; }
  }
}

Ответ 4

Используя undercore.js, вы можете взять ключи объекта и сделать трюк. Но я не уверен, что пары ключ-значение упорядочены в любом случае:

var next = function(db, key) {
    var keys = _.keys(db);
    var index = _.indexOf(keys, key);
    if(index+1<keys.length){
         return db[keys[index+1]];
    }else{
        return null;
    }
}

jsFiddle: http://jsfiddle.net/QWhN2/

Ответ 5

ts/es6 версия. Я просто получаю ключи от storeObject, ищите следующий индекс.

 let keys = Object.keys(storeObject);
 let nextIndex = keys.indexOf(theCurrentItem) +1;
 let nextItem = keys[nextIndex];