Сортировка массива по неалфавитным, заданным пользователем строковым значениям

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

[
  { target: random.text.cpu-pct-0, otherData[...] },
  { target: random.text.cpu-pct-1, otherData[...] },
  { target: random.text.cpu-pct-2, otherData[...] },
  { target: random.text.example-0, otherData[...] },
  { target: random.text.example-1, otherData[...] },
  { target: random.text.memory, otherData[...] },
  ...
]

Я хочу, чтобы все объекты с target, которые включают строку cpu-pct, были первыми, затем объекты с target, которые включают строку memory, затем example. В этом массиве может быть любое количество элементов, поэтому повторная сортировка по индексу не сработает. Может быть 1 объект с target, который включает cpu-pct, или их может быть 50+. То же самое касается других строк, по которым я сортирую.

Я подумал о том, чтобы перебрать исходный массив, проверить, существует ли нужная строка, сохранить соответствующие объекты в новые массивы для каждой цели, которую я ищу, а затем объединить массивы в конце. Я думаю, что это сработает, но я полагаю, что есть лучшее и более эффективное решение, возможно, с использованием usort, но я в растерянности. Есть идеи?


person Bryan    schedule 16.04.2018    source источник
comment
Так должен ли этот cpu-pct-0 появиться перед этим cpu-pct-1? И будет ли target всегда содержать один из cpu-pct, memery или example?   -  person fubar    schedule 17.04.2018
comment
Создайте пользовательскую функцию сравнения сортировки, используя php.net/manual/en/function.usort.php. Вы можете легко определить свои правила внутри одной функции. Эта функция должна возвращать -1, 0 или 1 в качестве ответа при сравнении двух элементов. Примеры на этой странице помогут. Сначала попробуйте это, а затем не стесняйтесь публиковать второй вопрос с примерами ваших попыток кода.   -  person Scuzzy    schedule 17.04.2018
comment
@fubar Да, cpu-pct-0 должно стоять первым и идти в порядке возрастания в соответствии с числом в конце. Забыл упомянуть, но также будет cpu-pct-avg, который должен появиться после других значений cpu-pct.   -  person Bryan    schedule 17.04.2018


Ответы (2)


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

$a = json_decode('[
  { "target": "random.text.cpu-pct-0", "otherData": [1, 2, 3] },
  { "target": "random.text.cpu-pct-1", "otherData": [2, 2, 3] },
  { "target": "random.text.cpu-pct-2", "otherData": [3, 2, 3] },
  { "target": "random.text.example-0", "otherData": [4, 2, 3] },
  { "target": "random.text.example-1", "otherData": [5, 2, 3] },
  { "target": "random.text.memory", "otherData": [6, 2, 3] } ]');

$ranks = array('example' => 0, 'memory' => 1, 'cpu-pct' => 2);

function rank($obj) {
    global $ranks;

    foreach ($ranks as $key => $value) {
        if (strpos($obj->target, $key) !== false) return $value;
    }
    // sort anything that doesn't match last
    return -1;
}

function cmp($a, $b) {
    return rank($b) - rank($a);
}

usort($a, "cmp");

print_r($a);

Выход:

Array
(
    [0] => stdClass Object
        (
            [target] => random.text.cpu-pct-0
            [otherData] => Array
                (
                    [0] => 1
                    [1] => 2
                    [2] => 3
                )

        )

    [1] => stdClass Object
        (
            [target] => random.text.cpu-pct-1
            [otherData] => Array
                (
                    [0] => 2
                    [1] => 2
                    [2] => 3
                )

        )

    [2] => stdClass Object
        (
            [target] => random.text.cpu-pct-2
            [otherData] => Array
                (
                    [0] => 3
                    [1] => 2
                    [2] => 3
                )

        )

    [3] => stdClass Object
        (
            [target] => random.text.memory
            [otherData] => Array
                (
                    [0] => 6
                    [1] => 2
                    [2] => 3
                )

        )

    [4] => stdClass Object
        (
            [target] => random.text.example-0
            [otherData] => Array
                (
                    [0] => 4
                    [1] => 2
                    [2] => 3
                )

        )

    [5] => stdClass Object
        (
            [target] => random.text.example-1
            [otherData] => Array
                (
                    [0] => 5
                    [1] => 2
                    [2] => 3
                )

        )

)

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

$ranks = array('cpu-pct', 'memory', 'example');

function rank($obj) {
    global $ranks;

    foreach ($ranks as $key => $value) {
        if (preg_match("/$value(.*)$/", $obj->target, $matches))
            return $key . $matches[1];
    }
    // sort anything that doesn't match last
    return 'z';
}

function cmp($a, $b) {
    return strcmp(rank($a), rank($b));
}

usort($a, "cmp");

Если у вас есть более 10 строк для сортировки, вам нужно будет изменить возвращаемое значение с rank на sprintf('%02d', $key) . $matches[1], чтобы сортировка работала правильно (замените 02 на столько цифр, сколько необходимо, чтобы гарантировать, что вы можете представить все строки сортировки в этом количестве цифр). ).

person Nick    schedule 16.04.2018
comment
Это хорошее решение, и оно работает для меня по большей части. Есть ли способ гарантировать, что все значения cpu-pct идут в порядке возрастания (cpu-pct-0, cpu-pct-1, cpu-pct-2 и т. д.), а затем cpu-pct-avg идут сразу после всех этих? Сейчас они все сгруппированы, но не в том порядке, в котором я хочу. Остальные элементы после группы cpu-pct сортируются правильно. - person Bryan; 18.04.2018
comment
Привет, Бретт, я обновил ответ функцией, которая также будет сортировать конечную часть строки. Обратите внимание, что это действительно зависит от вашего формата данных, если вы хотите получить больше соучастия, вам нужно будет сделать что-то в строках ответа @Scuzzy. - person Nick; 18.04.2018

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

$array = json_decode('[
  { "target": "random.text.cpu-pct-0" },
  { "target": "random.text.cpu-pct-1" },
  { "target": "random.text.cpu-pct-2"},
  { "target": "random.text.example-0" },
  { "target": "random.text.example-1" },
  { "target": "random.text.memory" }
]');

function mySortFunction( $one, $two )
{

  $pattern = '/\.(?<label>cpu-pct|example|memory)(?:-(?<value>\d+))?/';
  preg_match( $pattern, $one->target, $targetOne );
  preg_match( $pattern, $two->target, $targetTwo );

  // Both have CPU-PCT? then sort on CPU-PCT-VALUE
  if( $targetOne['label'] === 'cpu-pct' and $targetTwo['label'] === 'cpu-pct' )
  {
    return strcmp( $targetOne['value'], $targetTwo['value'] );
  }
  // Both have MEMORY? they are the same
  if( $targetOne['label'] === 'memory' and $targetTwo['label'] === 'memory' )
  {
    return 0;
  }
  // 1 has CPU but 2 has Memory, prefer CPU
  if( $targetOne['label'] === 'cpu-pct' and $targetTwo['label'] === 'memory' )
  {
    return -1;
  }
  // 1 has MEMORY but 2 has CPI, prefer CPU
  if( $targetOne['label'] === 'memory' and $targetTwo['label'] === 'cpu-pct' )
  {
    return 1;
  }
  // 1 is MEMORY or CPU, but 2 is Neither
  if( $targetOne['label'] === 'cpu-pct' or $targetOne['label'] === 'memory' )
  {
    if( $targetTwo['label'] !== 'cpu-pct' and $targetTwo['label'] !== 'memory' )
    {
      return -1;
    }
  }
  // 2 is MEMORY or CPU, but 1 is Neither
  if( $targetTwo['label'] === 'cpu-pct' or $targetTwo['label'] === 'memory' )
  {
    if( $targetOne['label'] !== 'cpu-pct' and $targetOne['label'] !== 'memory' )
    {
      return 1;
    }
  }
  // ETC
  // ETC
  // ETC
}

usort( $array, 'mySortFunction' );
person Scuzzy    schedule 16.04.2018