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

153

Возможно ли в PHP сделать что-то подобное? Как бы вы пошли о написании функции? Вот пример. Порядок это самая важная вещь.

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

И я хотел бы сделать что-то вроде

$properOrderedArray = sortArrayByArray($customer, array('name', 'dob', 'address'));

Потому что в конце я использую foreach (), и они не в правильном порядке (потому что я добавляю значения в строку, которая должна быть в правильном порядке, и я не знаю заранее все ключи массива / ценности).

Я просмотрел внутренние функции массива PHP, но кажется, что вы можете сортировать только по алфавиту или по номерам.

Алекс
источник

Ответы:

348

Просто используйте array_mergeили array_replace. Array_mergeработает, начиная с заданного вами массива (в правильном порядке) и перезаписывая / добавляя ключи данными из вашего фактического массива:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);
//Or:
$properOrderedArray = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

//$properOrderedArray -> array('name' => 'Tim', 'address' => '123 fake st', 'dob' => '12/08/1986', 'dontSortMe' => 'this value doesnt need to be sorted')

PS - я отвечаю на этот «устаревший» вопрос, потому что я думаю, что все циклы, приведенные в предыдущих ответах, излишни.

Darkwaltz4
источник
31
Это хорошо работает, если у вас есть строковые ключи, но не для числовых. Документы PHP: «Если входные массивы имеют одинаковые строковые ключи, то более позднее значение для этого ключа перезапишет предыдущее. Однако, если массивы содержат числовые ключи, более позднее значение не будет перезаписывать исходное значение, но будет прилагается «.
Болбол
7
Хорошо, но что, если ключи не существуют в значениях? Мне это нужно, но только если какой-то из ключей существует ... Наверное, тогда нужен foreach для него ...
Соломон Клоссон
5
для моего случая это array_replace вместо array_merge. array_merge объединяет оба значения вместо замены второго массива в упорядоченные ключи.
Неофреко
3
Я наткнулся на ваше решение пару лет назад, когда искал что-то другое - и подумал про себя: это чрезвычайно эффективно по сравнению с циклами. Теперь мне нужно ваше решение, и мне потребовался час поиска, чтобы найти его снова! Спасибо!
Майкл
4
Кроме того, если массив 'order' (т.е. массив ('name', 'dob', 'address')) имеет больше ключей, чем массив для сортировки, то дополнительный массив array_intersect полученного отсортированного массива с исходным массивом будет обрезан случайные ключи, которые были добавлены в array_merge.
BBE
105

Там вы идете:

function sortArrayByArray(array $array, array $orderArray) {
    $ordered = array();
    foreach ($orderArray as $key) {
        if (array_key_exists($key, $array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}
Эран Гальперин
источник
12
Таким образом, вы можете объединить 2 массива со знаком +? Я никогда не знал этого, я использовал array_merge()!
Алекс
3
Это лучше, чем использовать usort()или uasort()?
grantwparks
5
Вы должны вставить breakзаявление, как только значение будет найдено.
Адель
4
@alex Будьте очень осторожны при замене array_merge()на +оператор массива . Он объединяется по ключу (также для цифровых клавиш) и слева направо , а array_mergeобъединяется справа налево и никогда не перезаписывает числовые клавиши. Например, [0,1,2]+[0,5,6,7] = [0,1,2,7]пока array_merge([0,1,2],[0,5,6,7]) = [0,1,2,0,5,6,7]и ['a' => 5] + ['a' => 7] = ['a' => 5]но array_merge(['a' => 5], ['a' => 7]) = ['a' => 7].
грипп
Безопасно ли использовать +знак?
crmpicco
47

Как насчет этого решения

$order = array(1,5,2,4,3,6);

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
    5 => 'five',
    6 => 'six'
);

uksort($array, function($key1, $key2) use ($order) {
    return (array_search($key1, $order) > array_search($key2, $order));
});
Питер де Гроот
источник
1
Этому нужно больше голосов, другое не сработало / работает не очень хорошо, хотя в моем случае это работает нормально.
Нг Сек Лонг
Это не очень эффективно. Для каждого сравнения выполняются два линейных поиска в массиве. Если мы предположим, что временная сложность uksort () равна O(n * log n), то этот алгоритм работает O(n^2 * log(n)).
Оператор
36

Другой способ для PHP> = 5.3.0:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$customerSorted = array_replace(array_flip(array('name', 'dob', 'address')), $customer);

Результат:

Array (
  [name] => Tim
  [dob] => 12/08/1986
  [address] => 123 fake st
  [dontSortMe] => this value doesnt need to be sorted
)

Прекрасно работает со строковыми и числовыми клавишами.

ekuser
источник
2
+ Пока они оба работают, я обнаружил, array_replace()что намерение передавать лучше, чем array_merge().
Джейсон МакКрири
1
array_replaceтакже оставляет тип переменной нетронутым. Если бы одно из значений в вашем массиве было бы, (string) '1'и вы бы использовали +оператор, значение было бы преобразовано в(int) 1
halfpastfour.am
1
Это также работает с цифровыми клавишами ( array_merge()просто добавит их?). Логика очень хорошо объяснена здесь . Во- первых , array_flip()изменения значения в $ Заказ массива до ключей. Во-вторых , array_replace()заменяет значения первого массива значениями, имеющими те же ключи во втором массиве. Если вам нужно отсортировать массив по ключам от другого, вам даже не нужно его использовать array_flip.
aexl
23
function sortArrayByArray(array $toSort, array $sortByValuesAsKeys)
{
    $commonKeysInOrder = array_intersect_key(array_flip($sortByValuesAsKeys), $toSort);
    $commonKeysWithValue = array_intersect_key($toSort, $commonKeysInOrder);
    $sorted = array_merge($commonKeysInOrder, $commonKeysWithValue);
    return $sorted;
}
OIS
источник
14

Возьмите один массив в качестве вашего заказа:

$order = array('north', 'east', 'south', 'west');

Вы можете отсортировать другой массив на основе значений, используя array_intersectDocs :

/* sort by value: */
$array = array('south', 'west', 'north');
$sorted = array_intersect($order, $array);
print_r($sorted);

Или, в вашем случае, для сортировки по ключам используйте array_intersect_keyDocs :

/* sort by key: */
$array = array_flip($array);
$sorted = array_intersect_key(array_flip($order), $array);
print_r($sorted);

Обе функции сохранят порядок первого параметра и будут возвращать только значения (или ключи) из второго массива.

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

hakre
источник
Пересечение избавит от тех записей, которые он не знает заранее.
DanMan
1
Это неправильно для сортировки по ключам. array_intersect_key будет возвращать только значения из array1, а не array2
жуткий
Согласен с pavsid - пример array_intersect_key неверен - он возвращает значения из первого массива, а не из второго.
Джонатан Акино
10

Я использовал решение Darkwaltz4, но использовал array_fill_keysвместо array_flip, чтобы заполнить, NULLесли ключ не установлен $array.

$properOrderedArray = array_replace(array_fill_keys($keys, null), $array);
Баптист Бернард
источник
5

Без магии ...

$array=array(28=>c,4=>b,5=>a);
$seq=array(5,4,28);    
SortByKeyList($array,$seq) result: array(5=>a,4=>b,28=>c);

function sortByKeyList($array,$seq){
    $ret=array();
    if(empty($array) || empty($seq)) return false;
    foreach($seq as $key){$ret[$key]=$dataset[$key];}
    return $ret;
}
Йеноваи Матиас
источник
1
Это работает хорошо, спасибо, просто обновите, $datasetчтобы соответствовать имени параметра
kursus
3

Если у вас есть массив в вашем массиве, вам придется немного адаптировать функцию Эрана ...

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key => $value) {
        if(array_key_exists($key,$array)) {
                $ordered[$key] = $array[$key];
                unset($array[$key]);
        }
    }
    return $ordered + $array;
}
Boombastic
источник
2

Эта функция возвращает подчиненный и отсортированный массив на основе второго параметра $ keys

function array_sub_sort(array $values, array $keys){
    $keys = array_flip($keys);
    return array_merge(array_intersect_key($keys, $values), array_intersect_key($values, $keys));
}

Пример:

$array_complete = [
    'a' => 1,
    'c' => 3,
    'd' => 4,
    'e' => 5,
    'b' => 2
];

$array_sub_sorted = array_sub_sort($array_complete, ['a', 'b', 'c']);//return ['a' => 1, 'b' => 2, 'c' => 3];
Доглас
источник
1

PHP имеет функции, которые помогут вам в этом:

$arrayToBeSorted = array('west', 'east', 'south', 'north');
$order = array('north', 'south', 'east', 'west');

// sort array
usort($arrayToBeSorted, function($a, $b) use ($order){
    // sort using the numeric index of the second array
    $valA = array_search($a, $order);
    $valB = array_search($b, $order);

    // move items that don't match to end
    if ($valA === false)
        return -1;
    if ($valB === false)
        return 0;

    if ($valA > $valB)
        return 1;
    if ($valA < $valB)
        return -1;
    return 0;
});

Usort делает всю работу за вас, а array_search предоставляет ключи. array_search () возвращает false, когда не может найти соответствие, поэтому элементы, которых нет в массиве sort, естественным образом перемещаются в конец массива.

Примечание: uasort () упорядочит массив, не затрагивая отношения ключ => значение.

danielcraigie
источник
1
  • сортировать по запросу
  • сохранить для int-ключей (из-за array_replace)
  • не возвращать ключи не существуют в inputArray
  • (необязательно) фильтр ключей не существует в данном списке ключей

Код:

 /**
 * sort keys like in key list
 * filter: remove keys are not listed in keyList
 * ['c'=>'red', 'd'=>'2016-12-29'] = sortAndFilterKeys(['d'=>'2016-12-29', 'c'=>'red', 'a'=>3 ]], ['c', 'd', 'z']){
 *
 * @param array $inputArray
 * @param string[]|int[] $keyList
 * @param bool $removeUnknownKeys
 * @return array
 */
static public function sortAndFilterKeys($inputArray, $keyList, $removeUnknownKeys=true){
    $keysAsKeys = array_flip($keyList);
    $result = array_replace($keysAsKeys, $inputArray); // result = sorted keys + values from input + 
    $result = array_intersect_key($result, $inputArray); // remove keys are not existing in inputArray 
    if( $removeUnknownKeys ){
        $result = array_intersect_key($result, $keysAsKeys); // remove keys are not existing in keyList 
    }
    return $result;
}
зерна
источник
1

Первое предложение

function sortArrayByArray($array,$orderArray) {
    $ordered = array();
    foreach($orderArray as $key) {
        if(array_key_exists($key,$array)) {
            $ordered[$key] = $array[$key];
            unset($array[$key]);
        }
    }
    return $ordered + $array;
}

Второе предложение

$properOrderedArray = array_merge(array_flip(array('name', 'dob', 'address')), $customer);

Я хотел бы отметить, что оба эти предложения являются удивительными. Однако это яблоки и апельсины. Различия? Один не ассоциативный, а другой дружественный. Если вы используете 2 полностью ассоциативных массива, тогда массив merge / flip фактически объединит и перезапишет другой ассоциативный массив. В моем случае это не тот результат, который я искал. Я использовал файл settings.ini для создания массива порядка сортировки. Массив данных, который я сортировал, не должен был перезаписываться моим коллегой по сортировке. Таким образом, слияние массива уничтожит мой массив данных. Оба являются отличными методами, оба должны быть заархивированы в любом наборе инструментов для разработчиков. Исходя из ваших потребностей, вы можете обнаружить, что вам действительно нужны обе концепции в ваших архивах.

user1653711
источник
1

Я взял ответ от @ Darkwaltz4 для его краткости и хотел бы поделиться тем, как я адаптировал решение к ситуациям, когда массив может содержать разные ключи для каждой итерации, например:

Array[0] ...
['dob'] = '12/08/1986';
['some_key'] = 'some value';

Array[1] ...
['dob'] = '12/08/1986';

Array[2] ...
['dob'] = '12/08/1986';
['some_key'] = 'some other value';

и поддерживал «главный ключ» примерно так:

$master_key = array( 'dob' => ' ' ,  'some_key' => ' ' );

array_merge выполнил бы слияние в итерации Array [1] на основе $ master_key и произвело бы ['some_key'] = '', пустое значение, для этой итерации. Следовательно, array_intersect_key использовался для изменения $ master_key в каждой итерации следующим образом:

foreach ($customer as $customer) {
  $modified_key = array_intersect_key($master_key, $unordered_array);
  $properOrderedArray = array_merge($modified_key, $customer);
}
Студия Pageii
источник
0

Немного поздно, но я не смог найти способ, которым я это реализовал, эта версия требует закрытия, php> = 5.3, но может быть изменена не так:

$customer['address'] = '123 fake st';
$customer['name'] = 'Tim';
$customer['dob'] = '12/08/1986';
$customer['dontSortMe'] = 'this value doesnt need to be sorted';

$order = array('name', 'dob', 'address');

$keys= array_flip($order);
uksort($customer, function($a, $b)use($keys){
    return $keys[$a] - $keys[$b];
});
print_r($customer);

Конечно, 'dontSortMe' нужно отсортировать, и он может появиться первым в примере

DJules
источник