Сортировать ключи строкового типа массива по пользовательскому алфавиту?

Я хочу сортировать массивы по ключу в php, но алфавит, который я использую, не является обычным английским алфавитом — это созданный мной алфавит. Это возможно?

Мой алфавит:

$alphabet = "AjawbpfmnrhHxXsSqkgtTdD =";

Массив такой:

Array (
   [=k_0] => Array(
       [0] => DI.3,2 &dwA-nTr& @Hrw@
       [1] => mA
       [2] => =k
       [3] => Sfj,t
       [4] => =k
       [5] => pXr
       )
   [aA_2] => Array(
       [0] => DI.7,4 &dwA-nTr& @Hrw-smA-tA,wj@
       [1] => snD
       [2] => aA
       [3] => Sfj,t
       [4] => jt
       [5] => jt,w
       )
  [sqA_1] => Array(
       [0] => DI.6,18 &dwA-nTr& @nswt@
       [1] => ra
       [2] => sqA
       [3] => Sfj,t
       [4] => =s
       [5] => r
       )
   );

Итак, если я отсортирую этот массив по моему алфавиту, то массив с ключом [=k_0] должен быть в конце.


person Preys    schedule 14.06.2011    source источник
comment
Что вы подразумеваете под самодельным алфавитом? Как вы сопоставляете их? вам все еще не нужно целое число для его представления?   -  person Pwnna    schedule 15.06.2011
comment
какой-нибудь пример таких ключей, может быть?   -  person patapizza    schedule 15.06.2011
comment
алфавит $alphabet = AjawbpfmnrhHxXsSqkgtTdD   -  person Preys    schedule 15.06.2011
comment
алфавит $alphabet = AjawbpfmnrhHxXsSqkgtTdD =; массив выглядит следующим образом: Array ([=k_0] =› Array([0] =› DI.3,2 &dwA-nTr& @Hrw@ [1] =› mA [2] =› =k [3] =› Sfj,t [4] =› =k [5] =› pXr ) [aA_2] =› Array( [0] =› DI.7,4 &dwA- nTr& @Hrw-smA-tA,wj@ [1] =› snD [2] =› aA [3] =› Sfj,t [4] =› jt [5] =› jt,w ) [sqA_1] =› Array( [0] =› DI.6,18 &dwA-nTr& @nswt@ [1] =› ra [2] =› sqA [3] =› Sfj,t [4] =› =s [5] =› r ) ); поэтому, если я отсортирую этот массив по моему алфавиту, массив с ключом [=k_0] должен быть в конце.   -  person Preys    schedule 15.06.2011


Ответы (5)


Вы можете использовать функцию usort() и указать собственную логику сортировки.

См. пример на php.net.

Изменить: используйте uksort, а не usort. См. http://www.php.net/manual/en/function.uksort.php. Спасибо @Darien!

Слегка измененный пример с php.net — исходный код с добавленным отображением $alphabet:

function cmp($a, $b)
{
    // custom sort order - just swapps 2 and 3.
    $alphabet = array (1 => 1, 2 => 3, 3 => 2, 4 => 4, 5 => 5, 6=> 6);

    if ($alphabet[$a] == $alphabet[$b]) {
        return 0;
    }
    return ($alphabet[$a] < $alphabet[$b]) ? -1 : 1;
}

$a = array(3 => 'c' , 2 => 'b', 5 => 'e', 6 => 'f', 1=>'a');
uksort($a, "cmp");

foreach ($a as $key => $value) {
    echo "$key: $value\n";
}
person Ryan    schedule 14.06.2011
comment
Да - OP имеет пользовательские требования, которые напрямую не покрываются обычными функциями. Без подробной информации usort — отличная отправная точка. - person Ryan; 15.06.2011
comment
Я думаю, вы имеете в виду uksort(). @Oz: Да, я так думаю, поскольку другая альтернатива - создание пользовательской локали - кажется мне невероятно трудоемкой/излишней. - person Darien; 15.06.2011
comment
Вопрос был о сортировке по пользовательскому алфавиту, а не только о том, «как сортировать массив». Это не ответ, поэтому -1. - person OZ_; 15.06.2011
comment
@OZ_: Это положительный путь, учитывая предоставленную информацию. Если вы можете придумать лучший подход, опубликуйте его. - person Darien; 15.06.2011
comment
@ Дариен, ты не тот человек, который может указывать мне, что делать, так что расслабься. И у меня есть полный ответ на этот интересный вопрос, и только потому, что я знаю, как это сделать, я знаю, что use usort это не ответ. - person OZ_; 15.06.2011
comment
вышеупомянутая функция, конечно, работает, но в моем случае ключи представляют собой строки, которые должны быть отсортированы в соответствии с заданным алфавитом, например, сон => вечер должен наступать после бодрствования => утро, что дает массив (бодрствование => утро, сон => вечер ); но теперь это нужно делать не с английским алфавитом, а с другим алфавитом (AjawbpfmnrhHxXsSqkgtTdD=) - person Preys; 15.06.2011
comment
Я должен согласиться с тем, что было ранее прокомментировано. Этот ответ неадекватен с точки зрения сложности ключей. Это не решение, которое легко может быть полезно для решения заданного вопроса. - person mickmackusa; 02.03.2021

Учитывая ваш $alphabet = "AjawbpfmnrhHxXsSqkgtTdD"; и предполагая, что Aja и т. д. согласно вашему комментарию, преобразуйте каждый ключ в альтернативном алфавите в серию в известном алфавите, например. используйте отображение, например:

  your alphabet: AjawbpfmnrhHxXsSqkgtTdD
 'real'alphabet: abcdefghijklmnopqrstuvw

Таким образом, ключ 'Ajaw' => 'abcd' и 'fmnr' => 'ghij' и т. д. Затем ваши ключи превращаются во что-то, что вы можете сортировать, используя обычные функции php. Однако вам понадобится какой-то способ обработки символов, отсутствующих в вашем исходном алфавите.

Что-то подобное может сработать - вам понадобятся две функции преобразования (из вашего алфавита в «настоящий» алфавит и наоборот), а затем компаратор, например. uksort.

Мои два цента - спасибо за разъяснение первоначального вопроса.

person Ryan    schedule 14.06.2011
comment
Это кажется возможным для небольшого текста, но при обработке большого количества текстов это кажется долгим обходным путем, чтобы найти решение. Я ищу что-то вроде следующего в python: sorted_list = sorted(Unsorted_list, key=lambda (v, k): [alphabet.index(c) for c in v]) - person Preys; 15.06.2011
comment
Я согласен - это не особенно элегантное решение. - person Ryan; 15.06.2011
comment
Скомпилируйте себе PHP 6, он уже встроен. - person hakre; 15.06.2011
comment
@hakre, можете ли вы опубликовать ссылку на эту документацию по собственным функциям php? - person Vinicius.Silva; 16.12.2017
comment
@ Vinicius.Silva Не помню конкретно, может, это strtr? php.net/strtr — аналогия с PHP 6, должно быть, была шуткой, ее никогда не выпускали. . - person hakre; 19.12.2017

К счастью, в вашем пользовательском алфавите не больше символов, чем в списке однобайтовых латинских букв, поэтому перевод — это очень простой и читабельный процесс.

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

Код: (Демо)

$array = [
    '=k_0' => ['test1'],
    'aA_2' => ['test2'],
    'sqA_1' => ['test3'],
    '=kj_0' => ['test4'],
    'awA_2' => ['test5'],
    '= D_1' => ['test6'],
    'sq A_1' => ['test7'],
    'sqA_2' => ['test8'],
];

$trans = ['AjawbpfmnrhHxXsSqkgtTdD =', 'abcdefghijklmnopqrstuvwxy'];

uksort(
    $array,
    function ($a, $b) use ($trans) {
        return strtr($a, ...$trans) <=> strtr($b, ...$trans);
    }
);
var_export($array);

Выход:

array (
  'aA_2' => 
  array (
    0 => 'test2',
  ),
  'awA_2' => 
  array (
    0 => 'test5',
  ),
  'sqA_1' => 
  array (
    0 => 'test3',
  ),
  'sqA_2' => 
  array (
    0 => 'test8',
  ),
  'sq A_1' => 
  array (
    0 => 'test7',
  ),
  '=k_0' => 
  array (
    0 => 'test1',
  ),
  '=kj_0' => 
  array (
    0 => 'test4',
  ),
  '= D_1' => 
  array (
    0 => 'test6',
  ),
)

Начиная с PHP7.4 синтаксис можно сократить, используя синтаксис стрелочных функций.

uksort(
    $array,
    fn($a, $b) => strtr($a, ...$trans) <=> strtr($b, ...$trans)
);
person mickmackusa    schedule 02.03.2021
comment
@hakre вот так ^ - person mickmackusa; 02.03.2021

После отзыва от @micmackusa я обновил свой пример для работы с uksrot и полностью ответил на вопрос.

$order = str_split("AjawbpfmnrhHxXsSqkgtTdD");

uksort($arr, function ($a, $b) use ($order) {
    $posA = array_search($a, $order);
    $posB = array_search($b, $order);
    return $posA - $posB;
});

http://sandbox.onlinephpfunctions.com/code/9b6f39b30dcc932517bbe82608dd8a0c8d35b3da

-- исходный ответ с usort--

Вы можете использовать usort() с настраиваемым массивом заказов, например

$arr = array("w","b","m","n","x","x","z","T","T","A","A");

$order = array("A","j","a","w","b","p","f","m","n","r","h","H","x","X","s","S","q","k","g","t","T","d","D"," ","=");
    usort($arr, function ($a, $b) use ($order) {
        $posA = array_search($a, $order);
        $posB = array_search($b, $order);
        return $posA - $posB;
    });

(вы, вероятно, могли бы просто взорвать строку $order, чтобы она была приятнее)

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

И рабочий пример http://sandbox.onlinephpfunctions.com/code/3934aafe93377ec18549d326d65516408a4362>

person MrsPop88    schedule 03.07.2018
comment
Я не думаю, что эта техника работает так, как требуется. Пожалуйста, докажите, что это работает, предоставив онлайн-демонстрацию. - person mickmackusa; 02.03.2021
comment
@обновлено примером. Я больше не использую uksort и использую usort так, как предполагалось изначально - person MrsPop88; 03.03.2021
comment
ОП не хочет сортировать по значениям. Основная проблема с вашей демонстрацией/ответом заключается в том, что вы игнорируете требования OP и образцы данных. Этот ответ неверен. - person mickmackusa; 03.03.2021
comment
Обновлено сейчас, чтобы работать с ответом с примером - person MrsPop88; 03.03.2021
comment
В вашем фрагменте НЕ используются очень четко предоставленные многосимвольные строковые ключи OP. Этот ответ не учитывает сложность сравнения ключей длиннее одного символа. Если вы запутались, прочитайте мой недавно опубликованный и правильный ответ, учитывающий сложность входных данных OP. - person mickmackusa; 03.03.2021

См. этот код:

<?php

$arr = array('wr' => 1, 'wrS' => 6, 'wr,w' => 3, 'wr.w' => 4, 'wr-qA' => 2, 'wrs' => 5);

function compare_by_alphabet(array $alphabet, $str1, $str2)
{
    $l1 = strlen($str1);
    $l2 = strlen($str2);
    $c = min($l1, $l2);

    for ($i = 0; $i < $c; $i++)
    {
        $s1 = $str1[$i];
        $s2 = $str2[$i];
        if ($s1===$s2) continue;
        $i1 = array_search($s1, $alphabet);
        if ($i1===false) continue;
        $i2 = array_search($s2, $alphabet);
        if ($i2===false) continue;
        if ($i2===$i1) continue;
        if ($i1 < $i2) return -1;
        else return 1;
    }
    if ($l1 < $l2) return -1;
    elseif ($l1 > $l2) return 1;
    return 0;
}

function compare_keys_by_alphabet($a, $b)
{
    static $alphabet = array('-', ',', '.', 'A', 'j', 'a', 'w', 'b', 'p', 'f', 'm', 'n', 'r', 'h', 'H', 'x', 'X', 's', 'S', 'q', '‌​k', 'g', 't', 'T', 'd', 'D', '=', '/', '(', ')', '[', ']', '<', '>', '{', '}', '\'', '*', '#', 'I', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, '&', '@');
    return compare_by_alphabet($alphabet, $a, $b);
}

uksort($arr, 'compare_keys_by_alphabet');

print_r($arr);

Результат:

Array
(
    [wr] => 1
    [wr-qA] => 2
    [wr,w] => 3
    [wr.w] => 4
    [wrs] => 5
    [wrS] => 6
)
person OZ_    schedule 14.06.2011
comment
@OZ, можешь объяснить следующее. с массивом: $arr = array('wr' => 1, 'wrS' => 6, 'wr,w' => 3, 'wr.w' => 4, 'wr-qA' => 2, ' wrs' => 5); $алфавит = массив('-','\,','.','A','j','a','w','b','p','f','m', 'n', 'r', 'h', 'H', 'x', 'X', s', 'S', 'q', 'k', 'g', 't', 'T ','d','D','=','/','(',')','[',']','‹','›','{','}', '\'','*','#','I',1,2,3,4,5,6,7,8,9,0,'&','@'); вывод, который я получаю: Array('[wr.w] => 4', '[wrs] => 5', '[wr,w] => 3', '[wr] => 1', '[wr -qA] => 2', '[wrS] => 6'); но с моим алфавитом я хочу Array('[wr] => 1', '[wr-qA] => 2', '[wr,w] => 3', '[wr.w] => 4', '[wrs] => 5', '[wrS] => 6') - person Preys; 16.06.2011
comment
@Preys, в своем комментарии вы написали 2 одинаковых массива :) Просто отсортируйте первый массив по значениям: asort($arr); Но если вы хотите сортировать по ЗНАЧЕНИЯМ, а не по КЛЮЧАМ, вам не нужны никакие алфавиты, только функция asort. - person OZ_; 16.06.2011
comment
поработав с python, должно быть что-то, чего я не понимаю в php; результатом работы программы должен быть массив, в котором находятся разные массивы. Эти разные массивы должны быть отсортированы по ключу. Итак, ваша программа помещает в качестве первого массива wr.w => 4; я хочу, чтобы первый массив был wr => 1; потому что на основе алфавита wr предшествует wr.w, как в нашем алфавите «птица предшествует птицам». То, какие значения имеют разные массивы (1, 5 или 6), не должно иметь никакого значения. - person Preys; 16.06.2011
comment
@Preys, теперь я понял. Смотрите отредактированный ответ. Обратите внимание, что , в алфавите не следует экранировать обратной косой чертой. И напишите @nickname в комментарии, если хотите отправить уведомление об этом комментарии. - person OZ_; 18.06.2011