Получить следующую пару ключ-значение в объекте
Учитывая ключ, я хочу найти следующее свойство в объекте. Я не могу полагаться на ключи, которые нужно заказать или последовательные (они являются 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:)
Ответы
Ответ 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];