Solucionado (ver solução)
Solucionado
(ver solução)
10
respostas

Ordenação de array associativo

Olá colegas,

estou tentando ordenar um array associativo com a função usort(), pela chave "price", entretanto a saída não é a esperada. A função está trocando os valores das chaves e não está ordenandoo array, segue abaixo:

{
        "name": "1555 Malabia House Hotel",
        "latitude": "-34.591035804444125",
        "longitude": "-58.42752456665039",
        "price": 67.25
    },
    {
        "name": "3B Barranco's Bed & Breakfast",
        "latitude": "-12.093084",
        "longitude": "-77.046491",
        "price": 80.7
    },
    {
        "name": "A Montesinho",
        "latitude": "41.8023831",
        "longitude": "-6.6990195",
        "price": 67.25
    },
    {
        "name": "A Vida ? Bela",
        "latitude": "38.7009772",
        "longitude": "-9.2466874",
        "price": 67.25
    },
    {
        "name": "ACTITUD S.A. - Holiday Packages",
        "latitude": "32.5709117",
        "longitude": "-115.3519086",
        "price": 134.5
    },

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Insira aqui a descrição dessa imagem para ajudar na acessibilidadeO resultado acima não é o esperado. O que estou fazendo errado neste caso, alguém pode me ajudar ?

10 respostas

Oi Adriano,

O array que vc quer odernar é esse mesmo do print? Por que, na verdade, isso é um json e não um array em php. Tenta usar a função json_decode para transformar o json em array se o problema for realmente esse e depois vc tenta a orndenação.

Oi Adriano, como vai?

Testei aqui seu código e vi que o problema é por conta do escopo das variáveis no PHP, a odernação está correta, o porém é que ela só está tendo efeito sobre o array $hotels dentro da função orderHotelsByPrice, caso queira perceber issso, você pode adicionar um var_dump no array de hotéis dentro da função, você verá que o array foi ordenado. O problema é que o efeito dessa ordenação fica apenas dentro da função, ou seja, quando acessarmos o array de hotéis fora da função, ele não estará ordenado, para mudar esse comportamento podemos utilizar o endereço de memória do array como o parâmetro da função, desta forma, a ordenação irá afetar globalmente o valor do array, não apenas dentro da função.

Para passarmos o endereço de memória de uma variável como parâmetro de uma função, fazemos desta forma:

function orderHotelsByPrice(&$hotels)
{

}

Perceba que é preciso apenas adicionar o & antes da variável.

A função completa ficará assim:

function orderHotelsByPrice(&$hotels)
{
    usort($hotels, function ($hotel1, $hotel2)
    {
        return $hotel1['price'] <=> $hotel2['price'];
    });
}

Espero que te ajude.

Caso tenha quaisquer comentários ou dúvidas a respeito de alguma aula ou desafio, pode postar aqui no fórum que vamos te responder!

Abraços e bons estudos!

Estou bem Murilo,

Realizei a tentativa aqui com o método passando a referência da memória do array e mesmo assim a ordenação não está funcionando. Este array é proveniente de um Json e eu fiz um array_map para adicionar chaves ao array, depois estou tentando ordenar pela chave "price". Segue o código completo abaixo:

Route::get('/hotels', function (){
    $jsonHotels = \Illuminate\Support\Facades\Http::get('https://buzzvel-interviews.s3.eu-west-1.amazonaws.com/hotels.json')->json();
    $hotels = $jsonHotels["message"];

    $hotelsList = array_map(function($hotel){
        $hotel['name']  = $hotel[0];
        unset($hotel[0]);
        $hotel['latitude']  = $hotel[1];
        unset($hotel[1]);
        $hotel['longitude']  = $hotel[2];
        unset($hotel[2]);
        $hotel['price']  = (float) $hotel[3];
        unset($hotel[3]);
        return $hotel;
    }, $hotels);


    Search::orderHotelsByPrice($hotelsList);

    return $hotelsList;

});

Método orderHotelsByPrice:

 public static  function orderHotelsByPrice(&$hotels){
            usort($hotels, function ($hotel1, $hotel2){
                return $hotel1['price'] <=> $hotel2['price'];
            });
        }

Adriano, a sua função agora está correta.

Você pode postar o que aquela variável $hotels está retornando (no formato crú do php, sem "pretty")? Possa ser que tenha algo de estranho nela.

De qualquer forma, eu fiz uma requisição para o endereço em que vc está buscando os dados e peguei parte deles para fazer uma simulação, talvez assim você tenha algum gatilho para saber o que está ocorrendo. Fiz uma pequena modificação na parte do mapeamento apenas para o código ficar mais limpo, segue a simulação:

$json = '
{
    "success": true,
    "message": [
        [
            "1555 Malabia House Hotel",
            "-34.591035804444125",
            "-58.42752456665039",
            "67.25"
        ],
        [
            "3B Barranco\'s Bed & Breakfast",
            "-12.093084",
            "-77.046491",
            "80.7"
        ],
        [
            "A Montesinho",
            "41.8023831",
            "-6.6990195",
            "67.25"
        ],
        [
            "A Vida ? Bela",
            "38.7009772",
            "-9.2466874",
            "67.25"
        ],
        [
            "ACTITUD S.A. - Holiday Packages",
            "32.5709117",
            "-115.3519086",
            "134.5"
        ],
        [
            "Active Retreats Chalet",
            "45.9150555",
            "6.4142895",
            "107.6"
        ],
        [
            "Adagio",
            "41.3810897",
            "2.1750865",
            "107.6"
        ],
        [
            "Admiralty Beach House",
            "-33.9903176",
            "25.6651907",
            "121.05"
        ],
        [
            "Adria Hotel",
            "51.4944178",
            "-0.2284994",
            "80.7"
        ]
]
}
';

$arr = (json_decode($json, 1))['message'];


$mapArrToHotel = function ($arr) {
    return [
        'name' => $arr[0],
        'latitude' => $arr[1],
        'longitude' => $arr[2],
        'price' => $arr[3]
    ];
};


$hotelList = array_map(function($hotel) use ($mapArrToHotel){
    return $mapArrToHotel($hotel);
}, $arr);

usort($hotelList, fn ($first, $second) => $first['price'] <=> $second['price']);


var_dump($hotelList);

Nesse exemplo ficou ordenado corretamente do menor valor para o maior o valor.

Qualquer dúvida estou à disposição.

Diego,

Estou usando o mesmo código acima, entretanto a saída está embaralhada, não sei qual o motivo agora:

Route::get('/hotels', function (){
    $json = \Illuminate\Support\Facades\Http::get('https://buzzvel-interviews.s3.eu-west-1.amazonaws.com/hotels.json');

    $arr = (json_decode($json, 1))['message'];

    $mapArrToHotel = function ($arr) {
        return [
            'name' => $arr[0],
            'latitude' => $arr[1],
            'longitude' => $arr[2],
            'price' => $arr[3]
        ];
    };


    $hotelList = array_map(function($hotel) use ($mapArrToHotel){
        return $mapArrToHotel($hotel);
    }, $arr);



    usort($hotelList, fn ($first, $second) => $first['price'] <=> $second['price']);

    return $hotelList;

Saída:

{
name: "Sherpa Travel Exchange",
latitude: " LLC",
longitude: "37.7786871",
price: "-122.4212424"
},
{
name: "San Crist?bal de las Casas",
latitude: " Chiapas",
longitude: "16.7382472",
price: "-92.62648460000003"
},
{
name: "Natuga",
latitude: " Ecolodge Villas and Natural Reserve",
longitude: "9.280450522328723",
price: "-83.81916046142578"
},
{
name: "Silvia's Lodging Association",
latitude: null,
longitude: "38.7540779",
price: "-9.1750369"
},
{
name: "Sea and C Resort Dahab",
latitude: "28.519420475621015",
longitude: "34.51130747795105",
price: "13.45"
},
{
name: "PARADISE COUPLES NEGRIL",
latitude: "18.333064088620198",
longitude: "-78.33754193491814",
price: "13.45"
},
{
name: "Differential Flat",
latitude: "-19.9875936",
longitude: "-43.846311300000025",
price: "13.45"
},
{
name: "Hotel Timor",
latitude: "-8.55440296511183",
longitude: "125.57347297668457",
price: "13.45"
},

Você consegue postar aqui o que sua variável $arr está retornando? Deve ser alguma coisa com seus dados.

Rode o código o que postei na sua máquina para ver se a saída é como vc quer. Caso esteja de acordo é algum probleminha com seus dados.

Eu fiz a requisação com o postman pq já estava aberto aqui e copiei o json retornado, a única coisa que fiz de diferente nos dados foi escapar essa aspas simples "3B Barranco\'s Bed & Breakfast" para o php não acusar erro. Veja ai como seu array está sendo retornado e coloque aqui que aí resolvemos o problema.

Retorno com pretty da var $arr:

[
[
"1555 Malabia House Hotel",
"-34.591035804444125",
"-58.42752456665039",
"67.25"
],
[
"3B Barranco's Bed & Breakfast",
"-12.093084",
"-77.046491",
"80.7"
],
[
"A Montesinho",
"41.8023831",
"-6.6990195",
"67.25"
],
[
"A Vida ? Bela",
"38.7009772",
"-9.2466874",
"67.25"
],
[
"ACTITUD S.A. - Holiday Packages",
"32.5709117",
"-115.3519086",
"134.5"
],
[
"Active Retreats Chalet",
"45.9150555",
"6.4142895",
"107.6"
],

Sem pretty:

array(976) {
[0]=>
array(4) {
[0]=>
string(24) "1555 Malabia House Hotel"
[1]=>
string(19) "-34.591035804444125"
[2]=>
string(18) "-58.42752456665039"
[3]=>
string(5) "67.25"
}
[1]=>
array(4) {
[0]=>
string(29) "3B Barranco's Bed & Breakfast"
[1]=>
string(10) "-12.093084"
[2]=>
string(10) "-77.046491"
[3]=>
string(4) "80.7"
}
[2]=>
array(4) {
[0]=>
string(12) "A Montesinho"
[1]=>
string(10) "41.8023831"
[2]=>
string(10) "-6.6990195"
[3]=>
string(5) "67.25"
}
[3]=>
array(4) {
[0]=>
string(13) "A Vida ? Bela"
[1]=>
string(10) "38.7009772"
[2]=>
string(10) "-9.2466874"
[3]=>
string(5) "67.25"
}
[4]=>
array(4) {
[0]=>
string(31) "ACTITUD S.A. - Holiday Packages"
[1]=>
string(10) "32.5709117"
[2]=>
string(12) "-115.3519086"
[3]=>
string(5) "134.5"
}
[5]=>
array(4) {
[0]=>
string(22) "Active Retreats Chalet"
[1]=>
string(10) "45.9150555"
[2]=>
string(9) "6.4142895"
[3]=>
string(5) "107.6"
}
solução!

Adriano, acho que o problema está com o formato dos dados, nem todos estão seguindo o padrão desejado por você.

Eu notei que naquela saída de dados que vc postou, logo depois que te passei o código, tem um elemento que onde deveria ter a latitude tem um nome. Eu busquei aqui esse hotel e ele tem tem um indice a mais, é por isso que, provavelmente, está embaralhando tudo quando vai fazer o map. Deve ter outros dados com esse probleminha tb :

[
            "Natuga",
            " Ecolodge Villas and Natural Reserve",
            "9.280450522328723",
            "-83.81916046142578",
            "80.7"
        ],

Outro hotel daquela sua saída que tem indice a mais e vai causar embaralhamento dos valores:


 [
 "San Crist?bal de las Casas",
            " Chiapas",
            "16.7382472",
            "-92.62648460000003",
            "107.6"
    ]

Caso todos os dados estejam no formato "nome, longitude, latitude, preço" o código irá funcionar, caso contrário vai embaralhar tudo. A solução inicial que me vem na cabeça é ordernar os dados pelo último indice do array pq o último índice é sempre o preço.

Nessa última saída de dados que vc postou o código deve funcionar pq todos os dados estão no formato que vc deseja, verifique se é isso mesmo.

Aqui está o mesmo código com o array que vc passou no comentário acima:

$arr = [
    [
    "1555 Malabia House Hotel",
    "-34.591035804444125",
    "-58.42752456665039",
    "67.25"
    ],
    [
    "3B Barranco's Bed & Breakfast",
    "-12.093084",
    "-77.046491",
    "80.7"
    ],
    [
    "A Montesinho",
    "41.8023831",
    "-6.6990195",
    "67.25"
    ],
    [
    "A Vida ? Bela",
    "38.7009772",
    "-9.2466874",
    "67.25"
    ],
    [
    "ACTITUD S.A. - Holiday Packages",
    "32.5709117",
    "-115.3519086",
    "134.5"
    ],
    [
    "Active Retreats Chalet",
    "45.9150555",
    "6.4142895",
    "107.6"
    ]
];


$mapArrToHotel = function ($arr) {
    return [
        'name' => $arr[0],
        'latitude' => $arr[1],
        'longitude' => $arr[2],
        'price' => $arr[3]
    ];
};


$hotelList = array_map(function($hotel) use ($mapArrToHotel){
    return $mapArrToHotel($hotel);
}, $arr);

usort($hotelList, fn ($hotel1, $hotel2) => $hotel1['price'] <=> $hotel2['price']);


var_dump($hotelList);

Você pode copiar e rodar nesse compiler online https://paiza.io/en/projects/new se quiser agilizar e garantir o mesmo resultado que tive.

Muito Obrigado pelo apoio Diego,

Irei revisar estes dados aqui e tentar "limpar" pra garantir o correto funcionamento do código. Você indicaria outra forma de tratar esse Json para garantir a integridade dos dados? Valeu pelo direcionamento ! :)

Então, do jeito que o json está sendo retornado ai, sem poder filtrar campos na busca, terá que tratar os dados no map, mas isso depende de como vc quer.

Você quer manter os dados que tem aquele indice a mais ou quer removê-los? Outra questão também é que estamos supondo que existem esses dois formatos na busca, pode haver outros que não identificamos, mas vamos partir do principio que existem só esses dois mesmo. Outra coisa também, nomear as chaves é importante para o que vc quer fazer? Estou perguntando isso por que se não for importante pode pular essa etapa e ordenar os arrays comparando o valor do ultimo indice, que aparentemente parece ser sempre o preço.