it-swarm.com.ru

Сортировка массивов php по индивидуальному заказу

У меня есть массив массивов:

Array ( 
    [0] => Array (
        [id] = 7867867,
        [title] = 'Some Title'),
    [1] => Array (
        [id] = 3452342,
        [title] = 'Some Title'),
    [2] => Array (
        [id] = 1231233,
        [title] = 'Some Title'),
    [3] => Array (
        [id] = 5867867,
        [title] = 'Some Title')
)

Нужно идти в определенном порядке:

  1. 3452342
  2. 5867867
  3. 7867867
  4. 1231233

Как бы я поступил так? Я уже отсортировал массивы и прочитал множество других постов об этом, но они всегда основаны на сравнении (то есть valueA <valueB).

Помощь приветствуется.

44
Honus Wagner

Вы можете использовать usort(), чтобы точно определить порядок сортировки массива. В этом случае массив $order может использоваться в функции сравнения.

В приведенном ниже примере используется closure , чтобы упростить жизнь.

$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
    array('id' => 7867867, 'title' => 'Some Title'),
    array('id' => 3452342, 'title' => 'Some Title'),
    array('id' => 1231233, 'title' => 'Some Title'),
    array('id' => 5867867, 'title' => 'Some Title'),
);

usort($array, function ($a, $b) use ($order) {
    $pos_a = array_search($a['id'], $order);
    $pos_b = array_search($b['id'], $order);
    return $pos_a - $pos_b;
});

var_dump($array);

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

Функция сравнения работает путем нахождения позиций идентификаторов двух элементов для сравнения в массиве $order. Если $a['id'] предшествует $b['id'] в массиве $order, тогда возвращаемое значение функции будет отрицательным ($a меньше, поэтому «плавает» вверх). Если $a['id'] следует после $b['id'], то функция возвращает положительное число ($a больше, поэтому «падает» вниз). 

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

93
salathe

Расширение ответ Салате для этого дополнительного требования:

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

Вам нужно добавить два дополнительных условия в функцию сортировки:

  1. Предмет «не заботиться» должен считаться большим, чем предмет «заботиться о»
  2. Два предмета "не волнует" должны считаться равными

Таким образом, пересмотренный код будет:

$order = array(
    3452342,
    5867867,
    7867867,
    1231233
);
$array = array(
    array("id" => 7867867, "title" => "Must Be #3"),
    array("id" => 3452342, "title" => "Must Be #1"),
    array("id" => 1231233, "title" => "Must Be #4"),
    array("id" => 5867867, "title" => "Must Be #2"),
    array("id" => 1111111, "title" => "Dont Care #1"),
    array("id" => 2222222, "title" => "Dont Care #2"),
    array("id" => 3333333, "title" => "Dont Care #3"),
    array("id" => 4444444, "title" => "Dont Care #4")
);
function custom_compare($a, $b){
    global $order;
    $a = array_search($a["id"], $order);
    $b = array_search($b["id"], $order);
    if($a === false && $b === false) { // both items are dont cares
        return 0;                      // a == b
    }
    else if ($a === false) {           // $a is a dont care item
        return 1;                      // $a > $b
    }
    else if ($b === false) {           // $b is a dont care item
        return -1;                     // $a < $b
    }
    else {
        return $a - $b;
    }
}
shuffle($array);  // for testing
var_dump($array); // before
usort($array, "custom_compare");
var_dump($array); // after

Результат:

Before                         |  After
-------------------------------+-------------------------------
array(8) {                     |  array(8) {
  [0]=>                        |    [0]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(4444444)               |      int(3452342)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #4"  |      string(10) "Must Be #1"
  }                            |    }
  [1]=>                        |    [1]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(3333333)               |      int(5867867)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #3"  |      string(10) "Must Be #2"
  }                            |    }
  [2]=>                        |    [2]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(1231233)               |      int(7867867)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #4"    |      string(10) "Must Be #3"
  }                            |    }
  [3]=>                        |    [3]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(1111111)               |      int(1231233)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #1"  |      string(10) "Must Be #4"
  }                            |    }
  [4]=>                        |    [4]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(5867867)               |      int(2222222)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #2"    |      string(12) "Dont Care #2"
  }                            |    }
  [5]=>                        |    [5]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(2222222)               |      int(1111111)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #2"  |      string(12) "Dont Care #1"
  }                            |    }
  [6]=>                        |    [6]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(3452342)               |      int(3333333)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #1"    |      string(12) "Dont Care #3"
  }                            |    }
  [7]=>                        |    [7]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(7867867)               |      int(4444444)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #3"    |      string(12) "Dont Care #4"
  }                            |    }
}                              |  }
14
Salman A

Вам нужно определить свою собственную функцию сравнения и использовать usort или uasort , если вы хотите сохранить связь с индексом.

4
Nev Stokes

Другие ответы, использующие методы с повторяющимися вызовами array_search(), не так эффективны, как могли бы быть. Путем реструктуризации/переворота поискового массива order вы можете полностью пропустить все вызовы array_search(), что сделает вашу задачу намного более эффективной и короткой. Я буду использовать самый современный «оператор космического корабля» (<=>), но более ранние методы будут работать одинаково для линии сравнения.

Method # 1 - usort, когда все значения id существуют в $order ( Demo )

$order=array_flip([3452342,5867867,7867867,1231233]);  // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$array=[
    ['id'=>7867867,'title'=>'Some Title'],
    ['id'=>3452342,'title'=>'Some Title'],
    ['id'=>1231233,'title'=>'Some Title'],
    ['id'=>5867867,'title'=>'Some Title']
];

usort($array,function($a,$b)use($order){
    return $order[$a['id']]<=>$order[$b['id']];
    // when comparing ids 3452342 & 1231233, the actual comparison is 0 vs 3
});
// uasort() if you want to preserve keys

var_export($array);

Method # 2 - usort, когда некоторые значения id отсутствуют в $order ( Demo )
* примечание: isset() является менее дорогим вызовом, чем array_search()

$order=array_flip([3452342,5867867,7867867,1231233]);  // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$outlier=1+max($order);
// generating $outlier=4
$array=[
    ['id'=>7867867,'title'=>'Some Title'],
    ['id'=>3452342,'title'=>'Some Title'],
    ['id'=>'foo','title'=>'Some Title'],
    ['id'=>1231233,'title'=>'Some Title'],
    ['id'=>'bar','title'=>'Some Title'],
    ['id'=>5867867,'title'=>'Some Title']
];

usort($array,function($a,$b)use(&$order,$outlier){  // make $order modifiable with &
    if(!isset($order[$a['id']])){$order[$a['id']]=$outlier;}  // update lookup array with [id]=>[outlier number]
    if(!isset($order[$b['id']])){$order[$b['id']]=$outlier;}  // and again
    return $order[$a['id']]<=>$order[$b['id']];
});

var_export($array);

Альтернативный метод № 2 - usort, когда некоторые значения id не существуют в $order

... Я также хотел бы отметить, что в некоторых случаях избегание повторного двойного вызова isset() может быть менее привлекательным по сравнению с полной подготовкой массива $order перед вызовом usort().

Эта однострочная строка гарантирует отсутствие пропущенных значений id, что исключает необходимость чего-либо, кроме строки сравнения внутри функции сортировки. ( Полная демонстрация фрагмента )

$order=array_replace(array_fill_keys(array_column($array,'id'),$outlier),$order);
3
mickmackusa

@salathe Для тех из вас, кому трудно понять, что делает usort salathe: 

Каждый элемент в массиве $ является «чемпионом» в турнире, который находится в начале нового массива (за исключением того, что вместо номера один они хотят быть номером 0).

$ a - чемпион дома, а $ b - чемпион соперника в матче. 

$ pos_a и $ pos_b от обратного вызова - это то, какие атрибуты будут использоваться в борьбе за чемпиона a и b. В этом случае этот атрибут является индексом идентификатора чемпионов в $ order. 

Затем идет бой при возвращении. Теперь посмотрим, будет ли лучше иметь больше или меньше атрибутов. В битве usort чемпион дома хочет отрицательное число, поэтому он может быть быстрее в массиве. В гостях чемпион хочет положительное число. И если будет 0, это галстук. 

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

Код будет выглядеть так:

примечание: код запускается много раз так же, как в реальном турнире много битв, чтобы решить, кто первым доберется (т. е. 0/начало массива)

//tournament with goal to be first in array
    usort($champions, function ($home, $away) use ($order) {
        $home_attribute = array_search($a['id'], $order);
        $away_attribute = array_search($b['id'], $order);
        //fight with desired outcome for home being negative and away desiring positive
        return $home_attribute - $away_attribute;
    });
1
Jason Basanese

Без сортировки вы также можете получить это.

Это не дубликат идентификатора;

<?php

    $order = array(3452342, 5867867, 7867867, 1231233);
    $array = array(
        array('id' => 7867867, 'title' => 'Some Title'),
        array('id' => 3452342, 'title' => 'Some Title'),
        array('id' => 1231233, 'title' => 'Some Title'),
        array('id' => 5867867, 'title' => 'Some Title'),
    );

    $order = array_flip($order);
    $array = array_column($array,null,"id");
    $result = array_replace($order,$array);
    var_dump(array_values($result));

С повторяющимся идентификатором,

<?php

    $order = array(3452342, 5867867, 7867867, 1231233);
    $array = array(
        array('id' => 7867867, 'title' => 'Some Title'),
        array('id' => 3452342, 'title' => 'Some Title'),
        array('id' => 1231233, 'title' => 'Some Title'),
        array('id' => 5867867, 'title' => 'Some Title'),
    );

    $order_dict = array_flip($order);
    $order_dict = array_combine($order,array_fill(0,count($order),[]));
    foreach($array as $item){
        $order_dict[$item["id"]][] = $item;
    }
    //$order_dict = array_filter($order_dict);  // if there is empty item on some id in $order array
    $result = [];
    foreach($order_dict as $items){
        foreach($items as $item){
            $result[] = $item;
        }
    }
    var_dump($result);
0
Kris Roofe