Sort array as more than one condition

Asked

Viewed 89 times

1

Test scenario

I got the following array, where the records are related, ie the key of each level, is related to the other:

Array
(
    [regs] => Array
        (
            [c1] => Array
                (
                    [0] => 1
                    [1] => 2
                    [2] => 3
                    [3] => 2
                    [4] => 1
                )

            [c2] => Array
                (
                    [0] => 10
                    [1] => 20
                    [2] => 30
                    [3] => 20
                    [4] => 10
                )

            [c3] => Array
                (
                    [0] => 100
                    [1] => 200
                    [2] => 300
                    [3] => 200
                    [4] => 400
                )

            [c4] => Array
                (
                    [0] => 01:00
                    [1] => 02:00
                    [2] => 03:00
                    [3] => 04:00
                    [4] => 05:00
                )
        )
)

Array: array ( 'regs' => array ( 'c1' => array ( 0 => '1', 1 => '2', 2 => '3', 3 => '2', 4 => '1', ), 'c2' => array ( 0 => '10', 1 => '20', 2 => '30', 3 => '20', 4 => '10', ), 'c3' => array ( 0 => '100', 1 => '200', 2 => '300', 3 => '200', 4 => '400', ), 'c4' => array ( 0 => '01:00', 1 => '02:00', 2 => '03:00', 3 => '04:00', 4 => '05:00', ), ), )


Goal

I would like to "order" the array but maintaining the relationship of values.

Example:

Ordering by c1, c2, c3:

Array
(
    [regs] => Array
        (
            [c1] => Array
                (
                    [0] => 1
                    [1] => 1
                    [2] => 2
                    [3] => 2
                    [4] => 3
                )

            [c2] => Array
                (
                    [0] => 10
                    [1] => 10
                    [2] => 20
                    [3] => 20
                    [4] => 30
                )

            [c3] => Array
                (
                    [0] => 100
                    [1] => 400
                    [2] => 200
                    [3] => 200
                    [4] => 300
                )

            [c4] => Array
                (
                    [0] => 01:00
                    [1] => 05:00
                    [2] => 02:00
                    [3] => 04:00
                    [4] => 03:00
                )

        )

)

Bonus

Break and group values according to conditions.

Example: unite all values you have c1, c2, c3 equal:

Array
(
    [regs] => Array
        (
            [r1] => Array
                (
                    [c1] => Array
                        (
                            [0] => 1
                        )

                    [c2] => Array
                        (
                            [0] => 10
                        )

                    [c3] => Array
                        (
                            [0] => 100
                        )

                    [c4] => Array
                        (
                            [0] => 01:00
                        )

                )

            [r2] => Array
                (
                    [c1] => Array
                        (
                            [0] => 2
                            [1] => 2
                        )

                    [c2] => Array
                        (
                            [0] => 20
                            [1] => 20
                        )

                    [c3] => Array
                        (
                            [0] => 200
                            [1] => 200
                        )

                    [c4] => Array
                        (
                            [0] => 02:00
                            [1] => 04:00
                        )

                )

            [r3] => Array
                (
                    [c1] => Array
                        (
                            [0] => 3
                        )

                    [c2] => Array
                        (
                            [0] => 30
                        )

                    [c3] => Array
                        (
                            [0] => 300
                        )

                    [c4] => Array
                        (
                            [0] => 03:00
                        )

                )

            [r4] => Array
                (
                    [c1] => Array
                        (
                            [0] => 1
                        )

                    [c2] => Array
                        (
                            [0] => 10
                        )

                    [c3] => Array
                        (
                            [0] => 400
                        )

                    [c4] => Array
                        (
                            [0] => 05:00
                        )
                )
        )
)

  • There are native functions that do this automatically?
  • How could I make these "ordinations"?
  • You only need to go inside to go sorted by the sublevels of your array, going to sort the innermost levels until you go to the outermost last, making ifs in the desired conditions for sorting. It is not complex but can be confusing, research on ordering arrays in sublevels.

1 answer

3


To sort, you will need to group the values using the function array_map, make ordination with usort and then return the original structure with array_column.

For example, you have the following original array:

array (
  'regs' => 
  array (
    'c1' => 
    array (
      0 => '1',
      1 => '2',
      2 => '3',
      3 => '2',
      4 => '1',
    ),
    'c2' => 
    array (
      0 => '10',
      1 => '20',
      2 => '30',
      3 => '20',
      4 => '10',
    ),
    'c3' => 
    array (
      0 => '100',
      1 => '200',
      2 => '300',
      3 => '200',
      4 => '400',
    ),
    'c4' => 
    array (
      0 => '01:00',
      1 => '02:00',
      2 => '03:00',
      3 => '04:00',
      4 => '05:00',
    ),
  ),
)

You claim that the values of c1, c2, c3 and c4 are related to each other, so let’s group them in a more consistent structure with this relationship: let’s put all the values related to each other in the same array using the function array_map:

$agrupados = array_map(function($c1, $c2, $c3, $c4) {
    return compact('c1', 'c2', 'c3', 'c4');
}, $dados['regs']['c1'], $dados['regs']['c2'], $dados['regs']['c3'], $dados['regs']['c4']);

A simplified way to do the same would be to use the function array_values and the operator ...:

$agrupados = array_map(function($c1, $c2, $c3, $c4) {
    return compact('c1', 'c2', 'c3', 'c4');
}, ...array_values($dados['regs']));

The result would be:

array (
  0 => 
  array (
    'c1' => '1',
    'c2' => '10',
    'c3' => '100',
    'c4' => '01:00',
  ),
  1 => 
  array (
    'c1' => '2',
    'c2' => '20',
    'c3' => '200',
    'c4' => '02:00',
  ),
  2 => 
  array (
    'c1' => '3',
    'c2' => '30',
    'c3' => '300',
    'c4' => '03:00',
  ),
  3 => 
  array (
    'c1' => '2',
    'c2' => '20',
    'c3' => '200',
    'c4' => '04:00',
  ),
  4 => 
  array (
    'c1' => '1',
    'c2' => '10',
    'c3' => '400',
    'c4' => '05:00',
  ),
) 

Realize that now we have each array with the values associated with each other. Now we can sort according to the priority c1, c2 and c3:

usort($agrupados, function ($a, $b) {
    return $a['c1'] > $b['c1']
        or $a['c2'] > $b['c2']
        or $a['c3'] > $b['c3'];
});

This will order, but will still remain the arrays with the associated values:

array (
  0 => 
  array (
    'c1' => '1',
    'c2' => '10',
    'c3' => '100',
    'c4' => '01:00',
  ),
  1 => 
  array (
    'c1' => '1',
    'c2' => '10',
    'c3' => '400',
    'c4' => '05:00',
  ),
  2 => 
  array (
    'c1' => '2',
    'c2' => '20',
    'c3' => '200',
    'c4' => '02:00',
  ),
  3 => 
  array (
    'c1' => '2',
    'c2' => '20',
    'c3' => '200',
    'c4' => '04:00',
  ),
  4 => 
  array (
    'c1' => '3',
    'c2' => '30',
    'c3' => '300',
    'c4' => '03:00',
  ),
) 

To go back to the current structure, but keeping the sort, we can use the function array_column, that will return a array with all the values of that particular column:

$ordenado = [
    'c1' => array_column($agrupados, 'c1'),
    'c2' => array_column($agrupados, 'c2'),
    'c3' => array_column($agrupados, 'c3'),
    'c4' => array_column($agrupados, 'c4')
];

In this way, the result will be:

array (
  'c1' => 
  array (
    0 => '1',
    1 => '1',
    2 => '2',
    3 => '2',
    4 => '3',
  ),
  'c2' => 
  array (
    0 => '10',
    1 => '10',
    2 => '20',
    3 => '20',
    4 => '30',
  ),
  'c3' => 
  array (
    0 => '100',
    1 => '400',
    2 => '200',
    3 => '200',
    4 => '300',
  ),
  'c4' => 
  array (
    0 => '01:00',
    1 => '05:00',
    2 => '02:00',
    3 => '04:00',
    4 => '03:00',
  ),
) 

Which is the array in the original structure, but with the desired ordering.


Bonus

To group the way you want, you can use the version $agrupados of the previous solution going through all the values of the array and grouping those who have the same values of c1, c2 and c3. One simple way to generate this grouping is to generate a key in a array associative that represents these three values, so items that have the same values will have the same keys.

$resultado = [];

foreach($agrupados as $item) {
    // Gera uma chave única considerando os três valores
    $chave = "{$item['c1']}, {$item['c2']}, {$item['c3']}";

    // Se não existir, adiciona; caso contrário agrupa os valores
    if (!array_key_exists($chave, $resultado)) {
        $resultado[$chave] = $item;
    } else {
        foreach (array_keys($item) as $k) {
            if (!is_array($resultado[$chave][$k])) {
                $resultado[$chave][$k] = [$resultado[$chave][$k]];
            }

            $resultado[$chave][$k][] = $item[$k];
        }
    }
}

The result will be:

array (
  '1, 10, 100' => 
  array (
    'c1' => '1',
    'c2' => '10',
    'c3' => '100',
    'c4' => '01:00',
  ),
  '1, 10, 400' => 
  array (
    'c1' => '1',
    'c2' => '10',
    'c3' => '400',
    'c4' => '05:00',
  ),
  '2, 20, 200' => 
  array (
    'c1' => 
    array (
      0 => '2',
      1 => '2',
    ),
    'c2' => 
    array (
      0 => '20',
      1 => '20',
    ),
    'c3' => 
    array (
      0 => '200',
      1 => '200',
    ),
    'c4' => 
    array (
      0 => '02:00',
      1 => '04:00',
    ),
  ),
  '3, 30, 300' => 
  array (
    'c1' => '3',
    'c2' => '30',
    'c3' => '300',
    'c4' => '03:00',
  ),
) 

Note that only values in r2, who possess c1, c2 and c3 equal to 2, 20 and 200 respectively, which were grouped together. From this it is sufficient to rename the keys of the array end to r1, r2, etc as desired.

Browser other questions tagged

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