ChicagoDude
ChicagoDude

Reputation: 601

Sort a 2d array by values in a column

I have a php array that holds the following company records. Once created - I would like to arrange the array values or create a new array with all the companies listed order by distance (smallest to largest).

<?php
$company_values = array(
    array(
        'entitysymbol' => 'ANF',
        'distance'     => '0.42',
    ),

    array(
        'entitysymbol' => 'MSFT',
        'distance'     => '0.50',
    ),

    array(
        'entitysymbol' => 'GOOG',
        'distance'     => '0.78',
    ),
);

Upvotes: 2

Views: 544

Answers (3)

user1597430
user1597430

Reputation: 1146

The right answer:

usort($array, function ($a, $b)
{
    if (abs($a['distance'] - $b['distance']) <= 0.0001)
    {
        return 0; # equal
    }

    return $a['distance'] <=> $b['distance']; # swap $a and $b here for DESC order
});

I would like to highlight that when you deal with float values you should NOT compare them like integers. Mainly because float representation of any number, e.g. 0.1 can be stored as 0.0999999997 or 0.1000000003 in the same time. To avoid this problem you should introduce "epsilon" variable (which is here equals to 0.0001), so you'll be able to say "the difference between two float numbers is less than epsilon, so we should value them as equal numbers".

More info can be found here: Floating point numbers, refer to warning with a "Floating point precision" title.

Upvotes: 1

IhcAoh
IhcAoh

Reputation: 43

Give this a try:

function cmp($a, $b) {
    if ($a['distance'] == $b['distance']) {
        return 0;
    }
    return ($a['distance'] < $b['distance']) ? -1 : 1;
}

usort($company_values,"cmp");

var_dump($company_values);

For more information, take a read of usort function. Hope I've helped :)

Upvotes: 2

J. Quick
J. Quick

Reputation: 303

Here is some code which will do what you want :

<?php
// Define your array that needs sorting
$company_values = array(
    array(
        'entitysymbol' => 'MSFT',
        'distance'     => '0.60',
    ),
    array(
        'entitysymbol' => 'ANF',
        'distance'     => '0.42',
    ),

    array(
        'entitysymbol' => 'GOOG',
        'distance'     => '0.57',
    ),
    array(
        'entitysymbol' => 'MSFT',
        'distance'     => '0.50',
    ),
    array(
        'entitysymbol' => 'ANF',
        'distance'     => '0.30',
    ),

    array(
        'entitysymbol' => 'GOOG',
        'distance'     => '0.78',
    ),
    array(
        'entitysymbol' => 'MSFT',
        'distance'     => '0.50',
    ),
    array(
        'entitysymbol' => 'ANF',
        'distance'     => '0.42',
    ),

    array(
        'entitysymbol' => 'GOOG',
        'distance'     => '0.88',
    )
);

// Function to initialise a, b, and c before we start our search
function refresh_dichotomy($array) {
    $a = 0;
    $b = sizeof($array) - 1;
    $c = (int)(($a + $b) / 2);
    return [$a, $b, $c];
}

// Recursive dichotomy function to close in on where we need to insert the next element
function get_next_indexes($a, $b, $c, $array, $key, $value) {
    // If the value stored in the middle element is higher
    if($array[$c][$key] > $value) {
        // We lower the value of b
        $b = $c;
    // Otherwise
    } else {
        // We increse the value of a
        $a = $c;
    }
    // If a and b are not adjacent
    if($b - $a > 1) {
        // Get the middle value
        $c = (int)(($a + $b) / 2);
        // And dig deeper
        $a = get_next_indexes($a, $b, $c, $array, $key, $value);
    }
    return $a;
}

// Function to Fill the new array with the sorted values of the old one
function sort_array($old_array) {
    // Add the first element
    $new_array = Array($old_array[0]);
    // Cycle through the remaining elements
    for($i = 1; $i < sizeof($old_array); $i++) {
        // First check if the next element is smaller than the smallest value
        if($old_array[$i]['distance'] < $new_array[0]['distance']) {
            // If it is, then put it in first place
            array_splice($new_array, 0, 0, Array($old_array[$i]));
        }
        // Or if the next element is bigger than the biggest value
        else if($old_array[$i]['distance'] > $new_array[count($new_array) - 1]['distance']) {
            // If it is, then put it in last place
            array_splice($new_array, count($new_array), 0, Array($old_array[$i]));
        // Otherwise, find where we need to insert the next element
        } else {
            // Create 3 variables that will help us search via dichotomy
            $a;
            $b;
            $c;
            // Initialise their values to fit the current size of the new array
            $init = refresh_dichotomy($new_array);
            $a = $init[0];
            $b = $init[1];
            $c = $init[2];
            // Find where we need to insert our next value
            $a = get_next_indexes($a, $b, $c, $new_array, 'distance', $old_array[$i]['distance']);

            // Insert our new element
            array_splice($new_array, $a+1, 0, Array($old_array[$i]));
        }
    }
    return $new_array;
}

// Create an array to store the sorted values
$sorted_company_values = array();

// Sort !
$sorted_company_values = sort_array($company_values);

// Preview before and after
print_r($company_values);
echo("<br><br>");
print_r($sorted_company_values);

?>

This code creates a new array containing your sorted values, if you don't want a new array, replace :

$sorted_company_values = sort_array($company_values);

by

$company_values = sort_array($company_values);

It's basicly an implementation of a recursive insertion sort via dichotomy (more here) or binary search

Upvotes: 1

Related Questions