Как отсортировать многомерный массив по двум значениям с помощью оператора космического корабля? - PullRequest
0 голосов
/ 19 декабря 2018

Я пытаюсь отсортировать многомерный массив с помощью оператора космического корабля.

<?php

$my_array = [

    ['name'=>'cool', 'volume'=> 2, 'page'=>1],
    ['name'=>'sonic', 'volume'=> 1, 'page'=>1],
    ['name'=>'tails', 'volume'=> 3, 'page'=>1],
    ['name'=>'knuckles', 'volume'=> 1, 'page'=>2],
    ['name'=>'amy', 'volume'=> 1, 'page'=>3],
    ['name'=>'charmy', 'volume'=> 0, 'page'=>1]

];

usort($my_array, function ($a, $b) {
        return $a['page'] <=> $b['page'];
    });

var_dump($my_array);

Я могу сделать сравнение только с одним значением, однако как отсортировать, используя два значения (объем и страница)?

Ответы [ 4 ]

0 голосов
/ 19 декабря 2018

Интересный способ представить функциональное программирование -

# main.php

require 'comparison.php';

function sortByPage ($a, $b) {
  return $a['page'] <=> $b['page'];
}

function sortByVolume ($a, $b) {
  return $a['volume'] <=> $b['volume'];
}

// first sort by volume, then sort by page
usort($my_array, \comparison\concat (sortByVolume, sortByPage));

Выше concat может быть не сразу интуитивно понятным.Конечно, у вас есть concat две строки или два массива, и вы даже можете подумать о добавлении чисел, таких как 1 + 2, как concat (1, 2).Но как это выглядит для concat двух функций?

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

# comparison.php

namespace comparison;

require 'ordered.php';

function concat ($c1, $c2) {
  return function ($a, $b) use ($c1, $c2) {
    return \ordered\concat
      ( $c1 ($a, $b)
      , $c2 ($a, $b)
      );
  };
}

Функция сравнения принимает два аргумента и должна возвращать упорядоченный результат, (-1, 0или 1).Но так же, как мы можем реализовать concat для функций сравнения, мы можем также реализовать concat для упорядоченных результатов.Таким образом, мы передаем $a и $b каждой функции сравнения, $c1 и $c1, каждая из которых дает упорядоченное значение .Окончательный результат - concat из двух упорядоченных значений.

Так что же происходит, когда вы concat два упорядоченных значения?Вы получаете другое заказанное значение!

// ordered.php

namespace ordered;

function concat ($a, $b) {
  return $a === 0 ? $b : $a;
}

Выше всего необходимо все для достижения желаемого результата, но некоторые решения могут показаться произвольными.Возможно, вы также сочли утомительным определять sortByPage и sortByVolume как отдельные функции.Возможно, вам также понадобится возможность перевернуть сортировку, и это будет означать определение даже больше функций.Или, может быть, вам нужно отсортировать, используя больше , чем две функции сортировки.Расширение наших модулей comparison и ordered поможет.

order.php

PHP традиционно использует -1, 0 и 1 для кодирования упорядоченного результата.Мы можем быть более явными об этом в нашем модуле.Обратите внимание, что каждая функция проста, как и одна или две вещи.

namespace ordered;

const eq = 0;
const gt = 1;
const lt = -1;

function concat ($a, $b) {
  return $a === eq ? $b : $a;
}

function sum ($os) {
  return array_reduce ($os, '\ordered\concat', eq);
}

function reverse ($a) {
  return $a * -1;
}

сравнение.php

В этом модуле мы реализуем <=> как функцию compare вместо оператора.Обратите внимание, что мы также вернули заказанный lt, gt или eq вместо -1, 1 или 0. Это учитывает барьер абстракции и позволяет модулю order изменять свое представление, не затрагивая модуль сравнения .Еще раз подчеркнем, что каждая функция выполняет только одну или две вещи.

namespace comparison;

require 'ordered.php';

function compare ($a, $b) {
  if ($a < $b)
    return \ordered\lt; // respect abstraction barrier
  if ($a > $b)
    return \ordered\gt; // respect abstraction barrier
  else
    return \ordered\eq; // respect abstraction barrier
}

function concat ($c1, $c2) {
  return function ($a, $b) use ($c1, $c2) {
    return \ordered\concat // respect abstraction barrier
      ( $c1 ($a, $b)
      , $c2 ($a, $b)
      );
  };
}

function sum ($cs) {
  return array_reduce ($cs, '\comparison\concat', '\comparison\compare');
}

function reverse ($c) {
  return function ($a, $b) use ($c) {
    return \ordered\reverse ($c ($a, $b)); // respect abstraction barrier
  };
}

function contramap ($f, $g) {
  return function ($a, $b) use ($f, $g) {
    return $f
      ( $g ($a)
      , $g ($b)
      );
  };
}

Эти простые модули подходят практически для любой задачи сортировки.

1. Объедините любое числосортировщиков

// possibly based on form submission data
$sorters =
  [ 'sortByVolume'  // pre-defined function
  , 'sortByPage'    // pre-defined function
  , 'sortByName'    // pre-defined function
  ];

usort ($my_array, \comparison\sum ($sorters));

2. обратная сортировка

usort ($my_array, \comparison\reverse ('sortByVolume'));

3. анонимные сортировщики - не нужно определять sortByVolume, sortByPage и т. Д.

function sortBy ($key) {
  return \comparison\contramap
    ( '\comparison\compare'
    , function ($x) use ($key) { return $x[$key]; }
    );
}

usort
  ( $my_array
  , \comparison\concat
      ( sortBy ('volume') // sorter created ad hoc
      , sortBy ('page')   // same here
      )
  );

4. Возможность смешивать и сопоставлять эти эффекты и получать надежный результат - неоспоримое преимущество функционального программирования.

$sorters =
  [ 'sortByVolume',            // pre-defined function
  , sortBy ('page')            // anonymous sorter
  , reverse (sortBy ('name'))  // reverse sorter
  ];

usort
  ( $my_array
  , \comparison\sum ($sorters) // combine all sorters
  );

В итоге мы не использовали оператор космического корабля <=> в наших модулях.Функциональное программирование - это программирование с помощью функций, поэтому такие вещи, как операторы, ключевые слова и операторы, избегают использования простых функций.Причина этого в том, что функции могут быть легко объединены с использованием композиции, тогда как операторы и операторы не всегда могут быть объединены.

0 голосов
/ 19 декабря 2018

Не ответ на ваш вопрос, но я обычно делаю несколько значений, таких как:

array_multisort(array_column($my_array, 'page'),
                array_column($my_array, 'volume'), SORT_DESC, $my_array);
0 голосов
/ 19 декабря 2018
<?php

$my_array = [

    ['name'=>'cool', 'volume'=> 2, 'page'=>1],
    ['name'=>'cool', 'volume'=> 1, 'page'=>1],
    ['name'=>'cool', 'volume'=> 3, 'page'=>1],
    ['name'=>'cool', 'volume'=> 1, 'page'=>2],
    ['name'=>'cool', 'volume'=> 1, 'page'=>3],
    ['name'=>'cool', 'volume'=> 0, 'page'=>1]

];

usort($my_array, function ($a, $b) {
    $page = $a['page'] <=> $b['page'];
    $volume = $a['volume'] <=> $b['volume'];
    if(0 == $page){
        return $volume;
    }else{
        return $page;
    }
});

var_dump($my_array);
0 голосов
/ 19 декабря 2018

Если usort() является обязательным условием, вы можете сделать это следующим образом:

<?php

$my_array = [

    ['name'=>'cool', 'volume'=> 2, 'page'=>1],
    ['name'=>'cool', 'volume'=> 1, 'page'=>1],
    ['name'=>'cool', 'volume'=> 3, 'page'=>1],
    ['name'=>'cool', 'volume'=> 1, 'page'=>2],
    ['name'=>'cool', 'volume'=> 1, 'page'=>3],
    ['name'=>'cool', 'volume'=> 0, 'page'=>1]

];

usort($my_array, function ($a, $b) {
    $return = $a['page'] <=> $b['page'];
    return ( !$return ? $a['volume'] <=> $b['volume'] : $return );
});

var_dump($my_array);

Я не проверял это, и вы не предоставили образец желаемого результата, поэтому вам может потребоваться изменить нав соответствии с вашими потребностями.

https://www.the -art-of-web.com / php / sortarray / # section_3 содержит множество отличных сортировочных материалов.

...