Сортувати масив за клавішами на основі іншого масиву?


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, але, здається, ви можете сортувати лише в алфавітному чи цифровому форматі.

Відповіді:


347

Просто використовуйте 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 - я відповідаю на це "несвіже" питання, тому що думаю, що всі петлі, подані як попередні відповіді, є надмірними.


31
Це добре працює, якщо у вас є рядові клавіші, але не для числових. Документи PHP: "Якщо вхідні масиви мають однакові рядкові клавіші, то пізніше значення для цього ключа буде перезаписано попереднє. Якщо, однак, масиви містять цифрові ключі, пізніше значення не замінить початкове значення, але буде додається ».
болбол

7
Добре, але що робити, якщо ключі не існують у значеннях? Мені це потрібно, але лише за наявності будь-якого з ключів ... Напевно, тоді потрібно передбачити ...
Соломон Клоссон

5
для мого випадку це array_replace замість array_merge. array_merge поєднує обидва значення замість заміни другого масиву в упорядковані клавіші.
neofreko

3
Я наткнувся на ваше рішення пару років тому, шукаючи щось інше - і я подумав, що це надзвичайно ефективно в порівнянні з петлями. Зараз у мене є потреба у вашому рішенні, і мені знадобилася година пошуку, щоб знову знайти його! Дякую!
Майкл

4
Крім того, якщо масив "замовити" (тобто, масив ('ім'я', 'dob', 'адреса')) має більше клавіш, ніж масив для сортування, то додатковий масив_інтерсекту отриманого відсортованого масиву з вихідним масивом буде відрізаний бродячі ключі, додані в 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()!
alex

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
)

Чудово працює за допомогою рядкових та цифрових клавіш.


2
+ Поки вони обоє працюють, я виявив, що array_replace()краще передати наміри array_merge().
Джейсон МакКрірі

1
array_replaceтакож залишає змінний тип неушкодженим. Якби одним із значень у вашому масиві було б (string) '1'і ви використовували б +оператор, це значення було б перетворене на(int) 1
halfpastfour.am

1
Це також працює на цифрових клавішах ( array_merge()чи просто додати їх?). Логіка дуже добре пояснено тут . По-перше , array_flip()змінює значення масиву $ order на ключі. По-друге , 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;
}

14

Візьміть один масив як своє замовлення:

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

Ви можете сортувати інший масив на основі значень за допомогою array_intersectДокументів :

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

Або у вашому випадку для сортування за клавішами використовуйте array_intersect_keyДокументи :

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

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

Тому для цих двох стандартних випадків вам не потрібно самостійно писати функцію, щоб виконувати сортування / перестановку.


Перехрестя позбулося б тих записів, про які він не знає заздалегідь.
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;
}

2

Ця функція повертає під- та відсортований масив на основі клавіш $ $ другого параметра

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, коли не може знайти відповідність, тому елементи, які не входять у масив сортування, природно переміщуються до нижньої частини масиву.

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


1
  • сортувати за запитом
  • зберегти для int-ключів (через array_replace)
  • не повертаються ключі не існують у inputArray
  • (необов'язково) клавіші фільтра, яких немає в даному списку keyList

Код:

 /**
 * 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 повністю асоціативних масиви, то масив злиття / перевернення фактично злиється та замінить інший асоціативний масив. У моєму випадку це не ті результати, яких я шукав. Я створив файл settings.ini для створення масиву порядку сортування. Масив даних, який я сортував, не потрібно було записувати моїм асоціативним колегою по сортуванню. Таким чином об'єднання масиву знищило б мій масив даних. Обидва є чудовими методами, обидва потрібно архівувати в будь-якій панелі інструментів розробників. Виходячи з ваших потреб, ви можете виявити, що вам фактично потрібні обидва поняття в архівах.


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);
}

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" потрібно розібратися, і він може з’явитися першим у прикладі

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.