I'm trying to sort a multidimensional object, and, after looking on php.net, I get that I should write a function that I can then call via usort. I'm having some trouble with the syntax. I haven't ever written something this complicated before, and trying to figure it out feels like a mindbender...

I'm working with the array below. I want to filter out duplicate [n] values. But, and this is the tricky part for me, I want to keep the [n] value that has the smallest [d] value.

So, if I have (and this is simplified, the real array is at the end of this post):

Array ( 

[7777] => Array 
			( [0] => Array
				( [n] => '12345' [d] => 1 ) 
			  [1] => Array
			  	( [n] => '67890' [d] => 4 )
			)

[8888] => Array
			( [2] => Array
				( [n] => '12345' [d] => 10 )
			  [3] => Array
			  	( [n] => '67890' [d] => 2 )
			)
)

I want to filter out duplicate [n] values based on the [d] value, so that I wind up with this:

Array ( 

[7777] => Array 
			( [0] => Array
				( [n] => '12345' [d] => 1 ) 
			)

[8888] => Array
			  [3] => Array
			  	( [n] => '67890' [d] => 2 )
			)
)

I've tried writing different variations of the function cmp example posted on php.net, but I haven't been able to get any to work, and I think it's because I'm not altogether clear on how to traverse it using their example...

I've also tried abandoning the usort completely and just unsetting the array key, but I don't believe I have the syntax correct for that either.

This is the cmp function I tried:

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

And here's the "unset" that I tried:

$m = 1;
$v = 0;
foreach($ns as $key => $value)
	{
	foreach($value as $k2)
	{
	if($value[$m]['n'] ==	$value[$v]['n'] && $value[$m]['d'] > $value[$v]['d'])
	{
		unset($ns[$key][$m]);
	}
	$m++; $v++;
		}
}

But, neither worked... Anyway, here's the real array I'm trying to work with... Help is greatly appreciated!

Array
(
    [32112] => Array
        (
            [0] => Array
                (
                    [n] => '02124'
                    [d] => '0'
                )

        [1] => Array
            (
                [n] => '02124'
                [d] => '0.240101905123744'
            )

        [2] => Array
            (
                [n] => '11050'
                [d] => '0.441758632682761'
            )

        [3] => Array
            (
                [n] => '02186'
                [d] => '0.317514080260304'
            )
    )
[43434] => Array
    (
        [4] => Array
            (
                [n] => '02124'
                [d] => '5.89936971664429e-05'
            )

        [5] => Array
            (
                [n] => '02124'
                [d] => '0.145859264792549'
            )

        [6] => Array
            (
                [n] => '11050'
                [d] => '0.327864593457739'
            )

        [7] => Array
            (
                [n] => '11050'
                [d] => '0.312135345168295'
            )
      )
)

    php really have no good multisort function

    There is [man]array_multisort[/man]
    but it will not take an array and sort it by 'name' for example

    	$data = array(
    0 => array('name'=>'John', 'age'=>30,'city'=>'New York'),
    1 => array('name'=>'Maria','age'=>27,'city'=>'Paris'),
    2 => array('name'=>'Roger','age'=>33,'city'=>'London'),
    3 => array('name'=>'Betty','age'=>21,'city'=>'London'));

    Instead you need to make 3 arrays first:

    $name = array('John','Maria','Roger','Betty');
    $age  = array(30,27,33,21);
    $city = array('New York','Paris','London','London');

    Then do the array_multisort() and the result is in $data

    <?php
    
    array_multisort($name,SORT_DESC, $age, $data);
    echo '<pre>';
    var_export($data);
    
    ?>

    Now, this will not solve such multidim arrays as you have.
    But if you want a good function for sorting 2-dimensional data rows
    such rows with same structire as in Databasse results
    then you should really consider some function
    in the posted comment of http://php.net/manual/en/function.array-multisort.php

    There are a number of versions of this multisort of array rows.
    This is the version I use. It uses [man]eval[/man]

    <?php
    // A more inuitive way of sorting multidimensional arrays 
    // using array_msort() in just one line, 
    // you don't have to divide the original array into per-column-arrays
    // Case insensitive by the use of strtolower()
    
    $arr1 = array(
        array('id'=>1,'name'=>'aA','cat'=>'cc'),
        array('id'=>2,'name'=>'aa','cat'=>'dd'),
        array('id'=>3,'name'=>'bb','cat'=>'cc'),
        array('id'=>4,'name'=>'bb','cat'=>'dd')
    );
    
    $arr2 = array_msort($arr1, array('name'=>SORT_DESC, 'cat'=>SORT_ASC));
    
    //function
    function array_msort($array, $cols)
    {
        $colarr = array();
        foreach ($cols as $col => $order) {
            $colarr[$col] = array();
            foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
        }
        $eval = 'array_multisort(';
        foreach ($cols as $col => $order) {
            $eval .= '$colarr[\''.$col.'\'],'.$order.',';
        }
        $eval = substr($eval,0,-1).');';
        eval($eval);
        $ret = array();
        foreach ($colarr as $col => $arr) {
            foreach ($arr as $k => $v) {
                $k = substr($k,1);
                if (!isset($ret[$k])) $ret[$k] = $array[$k];
                $ret[$k][$col] = $array[$k][$col];
            }
        }
        return $ret;
    
    }
    
    ?>

      Let me check: you have an array (which I'll call $records) that contains arrays (which I'll call $pairs). The elements of these arrays are an "$nd" pair.

      E.g., from your sample data,

      $nd = array('n'=>'11050', 'd'=>.441758632682761);
      $pairs[2] = $nd;
      $records[32112] = $pairs;
      

      And what you want to do is remove all $nd pairs that have the same 'n' as another $nd pair, but with a higher 'd' - in other words, if you took all of the $nd pairs with the same 'n', you want to keep only the one with the smallest 'd'.

      I'm also assuming that you want to maintain the same relative order of what's left - I notice that within a record the 'd' values aren't strictly increasing. $record[32112][3]['d'], for example is less than $record[32112][2]['d'].

      I don't actually think sorting is necessary in this instance. First, there's the complication caused by the fact that these $nd pairs are grouped into records. But more significantly is that after sorting you're still in the position of having to traverse the structure looking for duplicates. So, not much gain there.

      Given the above, here's my first crack at the idea:

      function remove_extra_n($records)
      {
      	// We keep a list. Each entry in the list
      	// records the position of each $nd pair
      	// in the original data, as well as the
      	// data itself.
      	// We only keep one entry for each distinct
      	// 'n' value. If another one appears in the
      	// data, we look at 'd' values to decide
      	// which one to keep.
      	$best = array();
      	foreach($records as $id=>$pairs)
      	{
      		foreach($pairs as $i=>$nd)
      		{
      			extract($nd);
      			if(!isset($best[$n])    // It's new to me 
      			|| $best[$n]['d'] > $d) // I like this new one better.
      			{
      				$best[$n] = array(
      					'id'=>$id,
      					'i'=>$i,
      					'n'=>$n, // meh
      					'd'=>$d);
      			}
      		}
      	}
      	// Now we need to reassemble the $best data
      	// into the original format
      	$retained = array();
      	foreach($best as $entry)
      	{
      		$retained[$entry['id']][$entry['i']] = array(
      			'n' => $entry['n'],
      			'd' => $entry['d']);
      	}
      	return $retained;
      }
      
        Write a Reply...