Переход к предыдущему и следующему элементу в объекте 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'));