Javascript естественная сортировка массива / объекта и поддержание индекса связи - PullRequest
6 голосов
/ 29 сентября 2010

У меня есть массив элементов в Javascript следующим образом:

var users = Array();

users[562] = 'testuser3';
users[16] = 'testuser6';
users[834] = 'testuser1';
users[823] = 'testuser4';
users[23] = 'testuser2';
users[917] = 'testuser5';

Мне нужно отсортировать этот массив, чтобы получить следующий вывод:

users[834] = 'testuser1';
users[23] = 'testuser2';
users[562] = 'testuser3';
users[823] = 'testuser4';
users[917] = 'testuser5';
users[16] = 'testuser6';

Обратите внимание, как он сортируется по значению массива, и после сортировки массива сохраняется связь между значением и индексом (что очень важно). Я искал решение для этого, пытался сделать это, но ударил стену.

Между прочим, я знаю, что это технически не массив, так как это будет означать, что индексы всегда повторяются от 0 до n, где n + 1 - число подсчета, идущее от n. Как бы вы это ни определяли, требования к проекту все те же. Кроме того, если это имеет значение, я НЕ использую jquery.

Ответы [ 7 ]

23 голосов
/ 29 сентября 2010

Порядок элементов массива определяется индексом.Таким образом, даже если вы указываете значения в другом порядке, значения всегда будут храниться в порядке их индексов, а неопределенные индексы будут undefined:

> var arr = [];
> arr[2] = 2;
> arr[0] = 0;
> arr
[0, undefined, 2]

Теперь, если вы хотите сохранить паруиндекс и значение, вам понадобится другая структура данных, возможно, массив массива, подобный этому:

var arr = [
    [562, 'testuser3'],
    [16, 'testuser6'],
    [834, 'testuser1'],
    [823, 'testuser4'],
    [23, 'testuser2'],
    [917, 'testuser5']
];

Это можно отсортировать с помощью этой функции сравнения:

function cmp(a, b) {
    return a[1].localeCompare(b[1]);
}
arr.sort(cmp);

Результатэтот массив:

[
    [834, 'testuser1'],
    [23, 'testuser2'],
    [562, 'testuser3'],
    [823, 'testuser4'],
    [917, 'testuser5'],
    [16, 'testuser6']
]
7 голосов
/ 29 сентября 2010

Если я правильно понимаю вопрос, вы используете массивы таким образом, что они не предназначены для использования . На самом деле стиль инициализации

// Don't do this!
var array = new Array();
array[0] = 'value';
array[1] = 'value';
array[2] = 'value';

учит неверным вещам о природе и назначении массивов. Массив - это упорядоченный список элементов, проиндексированных от нуля до нуля. Правильный способ создания массива - использовать литерал массива :

var array = [
    'value',
    'value',
    'value'
]

Индексы подразумеваются в зависимости от порядка, в котором указаны позиции. Создание массива и установка users[562] = 'testuser3' подразумевает , что в списке есть по меньшей мере 562 других пользователя и у вас есть причина знать только 563-е в данный момент.

В вашем случае, индекс - это данные, и он не представляет порядок элементов в наборе . То, что вы ищете, это карта или словарь, представленный в JavaScript * обычный объект :

var users = {
    562: 'testuser3',
    16:  'testuser6',
    834: 'testuser1',
    823: 'testuser4',
    23:  'testuser2',
    917: 'testuser5'
}

Теперь ваш набор не имеет порядка , но имеет значимые ключи. Отсюда вы можете последовать совету galambalazs , чтобы создать массив ключей объекта:

var userOrder;
if (typeof Object.keys === 'function') {
    userOrder = Object.keys(users);
} else {
    for (var key in users) {
        userOrder.push(key);
    }
}

… затем сортируйте это:

userOrder.sort(function(a, b){
    return users[a].localeCompare(users[b]);
});

Вот демоверсия

3 голосов
/ 29 сентября 2010

Вы не можете упорядочить подобные массивы в Javascript.Лучше всего сделать карту для заказа .

order = new Array();
order[0] = 562;
order[1] = 16;
order[2] = 834;
order[3] = 823;
order[4] = 23;
order[5] = 917;

Таким образом, вы можете иметь любой заказ независимо от ключей в исходном массиве.Для сортировки массива используйте пользовательскую функцию сортировки .

order.sort( function(a, b) {
  if ( users[a] < users[b] ) return -1;
  else if ( users[a] > users[b] ) return 1;
  else return 0;
});

for ( var i = 0; i < order.length; i++ ) {
  // users[ order[i] ]
}

[ Demo ]

2 голосов
/ 29 сентября 2010

Используя идеи из комментариев, я придумал следующее решение. Функция NaturalSort - это то, что я нашел в Google, и я изменил ее для сортировки многомерного массива. По сути, я сделал массив пользователей многомерным, причем первый индекс - это идентификатор пользователя, а второй - имя пользователя. Итак:

users[0][0] = 72;
users[0][1] = 'testuser4';
users[1][0] = 91;
users[1][1] = 'testuser2';
users[2][0] = 12;
users[2][1] = 'testuser8';
users[3][0] = 3;
users[3][1] = 'testuser1';
users[4][0] = 18;
users[4][1] = 'testuser7';
users[5][0] = 47;
users[5][1] = 'testuser3';
users[6][0] = 16;
users[6][1] = 'testuser6';
users[7][0] = 20;
users[7][1] = 'testuser5';

Затем я отсортировал массив, чтобы получить следующий вывод:

users_sorted[0][0] = 3;
users_sorted[0][1] = 'testuser1';
users_sorted[1][0] = 91;
users_sorted[1][1] = 'testuser2';
users_sorted[2][0] = 47;
users_sorted[2][1] = 'testuser3';
users_sorted[3][0] = 72;
users_sorted[3][1] = 'testuser4';
users_sorted[4][0] = 20;
users_sorted[4][1] = 'testuser5';
users_sorted[5][0] = 16;
users_sorted[5][1] = 'testuser6';
users_sorted[6][0] = 18;
users_sorted[6][1] = 'testuser7';
users_sorted[7][0] = 12;
users_sorted[7][1] = 'testuser8';

Код для этого ниже:

function naturalSort(a, b) // Function to natural-case insensitive sort multidimensional arrays by second index
{

    // setup temp-scope variables for comparison evauluation
    var re = /(-?[0-9\.]+)/g,
        x = a[1].toString().toLowerCase() || '',
        y = b[1].toString().toLowerCase() || '',
        nC = String.fromCharCode(0),
        xN = x.replace( re, nC + '$1' + nC ).split(nC),
        yN = y.replace( re, nC + '$1' + nC ).split(nC),
        xD = (new Date(x)).getTime(),
        yD = xD ? (new Date(y)).getTime() : null;
    // natural sorting of dates
    if ( yD )
        if ( xD < yD ) return -1;
        else if ( xD > yD ) return 1;
    // natural sorting through split numeric strings and default strings
    for( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
        oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
        oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
        if (oFxNcL < oFyNcL) return -1;
        else if (oFxNcL > oFyNcL) return 1;
    }
    return 0;
}

// Set values for index
    var users = Array();
    var temp = Array();

    users.push(Array('72', 'testuser4'));
    users.push(Array('91', 'testuser2'));
    users.push(Array('12', 'testuser8'));
    users.push(Array('3', 'testuser1'));
    users.push(Array('18', 'testuser7'));
    users.push(Array('47', 'testuser3'));
    users.push(Array('16', 'testuser6'));
    users.push(Array('20', 'testuser5'));

// Sort the array
    var users_sorted = Array();
    users_sorted = users.sort(naturalSort);
1 голос
/ 29 сентября 2010

Разреженные массивы обычно означают проблемы.Вам лучше сохранять пары ключ-значение в массиве как объекты (этот метод также допустим в формате JSON):

users = [{
    "562": "testuser3"
},{
    "16": "testuser6"
}, {
    "834": "testuser1"
}, {
    "823": "testuser4"
}, {
    "23": "testuser2"
}, {
    "917": "testuser5"
}];

Как и предполагалось, вы можете использовать цикл for для сопоставления функции сортировки с массивом.

1 голос
/ 29 сентября 2010

Я бы использовал карту один раз, чтобы создать новый массив пользователей, затем второй раз, чтобы вернуть нужную строку из нового массива.

var users= [];
users[562]= 'testuser3';
users[16]= 'testuser6';
users[834]= 'testuser1';
users[823]= 'testuser4';
users[23]= 'testuser2';
users[917]= 'testuser5';

var u2= [];
users.map(function(itm, i){
    if(itm){
        var n= parseInt(itm.substring(8), 10);
        u2[n]= i;
    }
});
u2.map(function(itm, i){
    return 'users['+itm+']= testuser'+i;
}).join('\n');

/*returned value: (String)
users[834]= testuser1
users[23]= testuser2
users[562]= testuser3
users[823]= testuser4
users[917]= testuser5
users[16]= testuser6
*/

Если вы хотите избежать пробелов. используйте простой фильтр на выходе -

u2.map(function(itm, i){
    return 'users['+itm+']= testuser'+i;
}).filter(function(itm){return itm}).join('\n');
0 голосов
/ 29 сентября 2010

Array.prototype.sort() принимает необязательную пользовательскую функцию сравнения - поэтому, если вы скопируете все свои users в массив таким образом [ [562, "testuser3"], [16, "testuser6"] ... etc.]

Затем sort этот массив со следующей функцией:

function(comparatorA, comparatorB) {
    var userA = comparatorA[1], userB = comparatorB[1]
    if (userA > userB)     return 1;
    if (userA < userB)     return -1;
    if (userA === userB)   return 0;
}

Затем восстановите ваш users объект. (Это приведет к потере сортировки.) Или сохраните данные во вновь отсортированном массиве массивов, если это будет работать для вашего приложения.

...