Переход к предыдущему и следующему элементу в объекте Javascript
Я хочу перемещаться вверх и вниз по объекту JavaScript, задав элемент объекта. Пример объекта:
var items = {
"5" : ["name", "link2"],
"8" : ["name 2", "link 2"],
"11" : ["name 3", "link 3"]
}
Теперь, если у меня есть элемент items["8"]
. Я хочу получить предыдущий и следующий пункт соответственно.
getItemBefore = function(index) {
// index holds 8
// should return "5"
}
getItemAfter = function(index) {
// index hold 8
// should return 11
}
Как я могу получить эти элементы?
Ответы
Ответ 1
Ключи, которые хранятся в объекте, можно получить в виде массива с помощью Object.keys(<var>)
. Порядок ключей в массиве произволен; вам нужно отсортировать их перед индексированием. Параметр - это встроенный метод sort()
для массивов, что особенно полезно, поскольку могут быть предоставлены пользовательские функции сравнения (см. Ниже). Заказ по умолчанию - в алфавитном порядке.
Как только вы получите упорядоченный массив, вам нужно только посмотреть, где находится ваш элемент в массиве, и вернуть из него следующий и предыдущие элементы:
var keys = Object.keys(items).sort();
var loc = keys.indexOf(item);
Учитывая, что loc > -1
(т.е. элемент существует):
- Предыдущий элемент:
items[keys[loc-1]]
, но убедитесь, что loc > 0
(это не первый).
- Следующий элемент:
items[keys[loc+1]]
, но проверьте, что loc < keys.length
(это не последний).
Object.keys
совместим с Javascript 1.85+; здесь является обходным решением для старых браузеров.
Альтернативные порядки
Числовой
Если вы хотите, чтобы клавиши имели числовой порядок, используйте эту функцию сравнения:
var keys = Object.keys(items).sort( function(a,b) {
return b - a;
});
Время создания (или модификации)
Если вы хотите работать с порядком создания, а не буквенно-цифровым, элементы должны сохранять свое время создания. Что-то вроде:
<value>.index = Date.getTime();
items['<item>'] = <value>;
Затем методу sort()
требуется следующая функция сравнения:
var keys = Object.keys(items).sort( function(a,b) {
return b.index - a.index;
});
Это может быть легко распространено на последнее упорядочение модификации или подобное.
Порядок создания
Обратите внимание, что первое решение работает только в том случае, если элементы создаются на расстоянии более 1 мс, что подходит для действий пользователя. Если элементы добавляются быстрее, используйте это вместо метки времени:
<value>.index = Object.keys(items).length;
Или, наоборот, сохраните внешний счетчик с количеством элементов в объекте.
Ответ 2
Этот ответ основывается на ответе @lemonzi, см. его ответ для более подробные объяснения.
Я просто хотел добавить рабочий пример для всех, кто борется с этим:
var items = {
"5": ["name", "link2"],
"8": ["name 2", "link 2"],
"11": ["name 3", "link 3"]
};
var getItem = function(key, i) {
var keys = Object.keys(items).sort(function(a,b){return a-b;});
var index = keys.indexOf(key);
if ((i==-1 && index>0) || (i==1 && index<keys.length-1)) {index = index+i;}
return items[keys[index]];
}
console.log(getItem("8",-1));
console.log(getItem("8",+1));
Ответ 3
Это немного сложно, потому что технически согласно спецификациям ECMAScript порядок свойств в объекте специфичен для реализации. Это означает, что на вашем языке нет гарантии, что ваши свойства будут в одном порядке при каждом доступе к ним. Если вы полагаетесь на это, там может быть проблема.
Смотрите ошибка в Chrome. В нем говорится, что Chrome не обещает вернуть ваши ключи в каком-либо конкретном порядке, и он был отмечен "WONTFIX" в течение 4-5 лет. Это означает, что вы не можете полагаться на это поведение в Chrome и здесь другое сообщение, указывающее, что теперь IE9 имеет ту же "проблему".
Итак, я бы рекомендовал вам создать свой собственный объект для управления вашими ключами и использовать объект JavaScript за кулисами для хранения ваших ключей.
Здесь что-то я сбросил вместе, он не поддерживает удаление, и он не имеет никаких ограничений, но он должен служить вашим целям. Заклинание здесь.
function orderedObject()
{
this.keys = [];
this.keyIndex = {};
this.store = {};
}
orderedObject.prototype.addItem = function(key,value)
{
this.keyIndex[key] = this.keys.length;
this.keys.push(key);
this.store[key] = value;
}
orderedObject.prototype.getItem = function(key)
{
return this.store[key];
}
orderedObject.prototype.getKeyBefore = function(key)
{
return this.keys[this.keyIndex[key] - 1];
}
orderedObject.prototype.getKeyAfter = function(key)
{
return this.keys[this.keyIndex[key] + 1];
}
var testObject = new orderedObject();
testObject.addItem("5" , ["name", "link2"]);
testObject.addItem("8" , ["name 2", "link 2"]);
testObject.addItem("11" , ["name 3", "link 3"]);
console.log(testObject.getKeyBefore("8"))
console.log(testObject.getKeyAfter("8"))
Ответ 4
Будет ли более подходящим массив объектов? Пример ниже может быть обернут в объект и сделать методы nextItem() и prevItem(). При этом вам также может потребоваться проверка границ.
var items = [];
items[0] = {index : '5', name : 'name', link : 'link2'};
items[1] = {index : '8', name : 'name 2', link : 'link 2'};
items[2] = {index : '11', name : 'name 3', link : 'link 3'};
//then access them like this
var nextItem = function (index) {
var i = 0,
max = items.length;
for (i; i < max; i += 1) {
if (items[i].index === index) {
return items[i + 1];
}
}
return 'not found';
};
var prevItem = function (index) {
var i = 0,
max = items.length;
for(i; i < max; i += 1) {
if (items[i].index === index) {
return items[i - 1];
}
}
return 'not found';
};
//the nextItem object after 5 is shown in the console.
console.dir(nextItem('5'));