Advanced sorting of array items

Asked

Viewed 153 times

2

I have an advanced report, which in the end processes the classification of the items, based on their score:

// Variável $relatório contém dados levantados e calculados em blocos para N itens. Cada bloco possui uma pontuação total do mesmo.
//  Ex: $relatorio[0]['consolidado']['bloco_1']['total_pontos'],  $relatorio[0]['consolidado']['bloco_2']['total_pontos'], $relatorio[0]['consolidado']['bloco_3']['total_pontos'], $relatorio[0]['consolidado']['bloco_N']['total_pontos'], ...

$classificacao_items = array();
foreach ($relatorio as $relatorio_item) {
    $classificacao_items[] = array(
        'cod_item' => $relatorio_item['item']['cod_item'],
        'item' => $relatorio_item['item']['descricao'],
        'etapa_num' => $relatorio_item['consolidado']['etapa_num'], // etapa_num é um índice do relatório consolidado que recebe um valor de 0 a 5 (varia de acordo com média de metas atingidas do item)
        'pontos' => $relatorio_item['consolidado']['total_pontos'],
    );
}

Currently, I raise the classification using the array Multisort

$classificacao_items = array_orderby($classificacao_items, 'etapa_num', SORT_ASC, 'pontos', SORT_DESC, 'item', SORT_ASC);

As you can see, I order first by etapa_num, then by pontos and finally by item (Item name, alphabetical).

Snippet of the function:

/**
 * Função de ordenação de arrays
 * @see http://www.php.net/manual/en/function.array-multisort.php#100534
 * @return mixed
 */
function array_orderby() {
    $args = func_get_args();
    $data = array_shift($args);
    foreach ($args as $n => $field) {
        if (is_string($field)) {
            $tmp = array();
            foreach ($data as $key => $row)
                $tmp[$key] = $row[$field];
            $args[$n] = $tmp;
        }
    }
    $args[] = &$data;

    call_user_func_array('array_multisort', $args);

    return array_pop($args);
}

What I need now: if there is a point draw, I need to examine the sum of blocks (1 and 2 in the case) of the items and give a better ranking for an item (adding block 1 and 2), because the current tie-breaker parameter would be its name. That is, you would need to apply a custom function.

My problem is that I’m not being able to successfully apply this custom function. It ends up destroying the organization of items already correctly classified.

// Variável $items == variável $relatorio (descrito acima na introdução), ou seja, apenas com outro "apelido"
usort($classificacao_items, function($a, $b) use ($items) {
    if ($a['pontos'] != $b['pontos']) // na lógica quando retorna 0 não é pra modificar a posição do item
        return 0;

    $relatorio_a = $items[$a['cod_item']]['consolidado'];
    $relatorio_b = $items[$b['cod_item']]['consolidado'];

    $notas_a = $relatorio_a['bloco_1']['total_pontos'] + $relatorio_a['bloco_2']['total_pontos'];
    $notas_b = $relatorio_b['bloco_1']['total_pontos'] + $relatorio_b['bloco_2']['total_pontos'];

    return $notas_a  > $notas_b ? 1 : -1;
});

At the moment, I can’t see a viable solution to my problem. Every approach I try, I end up disorganizing with the array order. I need to apply this tiebreaker function only in cases that the score is equal and does not change what is right (with more or less score).

1 answer

4


You can’t put all the sort logic inside the custom method?

usort($classificacao_items, function($a, $b) use ($items) {

    // 'etapa_num', SORT_ASC
    if ($a['etapa_num'] < $b['etapa_num']) return -1;
    if ($a['etapa_num'] > $b['etapa_num']) return 1;

    // 'pontos', SORT_DESC
    if ($a['pontos'] > $b['pontos']) return -1;
    if ($a['pontos'] < $b['pontos']) return 1;

    // 'item', SORT_ASC
    if ($a['item'] < $b['item']) return -1;
    if ($a['item'] > $b['item']) return 1;

    $relatorio_a = $items[$a['cod_item']]['consolidado'];
    $relatorio_b = $items[$b['cod_item']]['consolidado'];

    $notas_a = $relatorio_a['bloco_1']['total_pontos'] + $relatorio_a['bloco_2']['total_pontos'];
    $notas_b = $relatorio_b['bloco_1']['total_pontos'] + $relatorio_b['bloco_2']['total_pontos'];

    if ($notas_a < $notas_b) return -1;
    if ($notas_a > $notas_b) return 1;

    return 0;
});
  • Functional and practical, more than accept solution!

  • I’m happy to help!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.