PHP nesne özelliği Sıralama dizi?

13 Cevap php

Ben gibi bir nesne varsa:

class Person {
  var $age;
  function __construct($age) {
    $this->age = $age;
  }
}

ve Person s herhangi bir dizi var

$person1 = new Person(14);
$person2 = new Person(5);
$people = array($person1, $person2);

Person->age özelliği ile $people dizi sıralamak için kolay bir yol var mı?

13 Cevap

Ben bir usort çalıştı ve yaklaşık 1,8 saniye içinde 15000 kişi nesneleri sınıflandırılmaktadır.

Eğer karşılaştırma işlevi aramaların verimsizlik endişe gibi, ben olmayan bir özyinelemeli Quicksort uygulanması ile karşılaştırdık. Bu aslında zamanın üçte biri, yaklaşık 0,5 saniye etrafında koştu.

Burada iki yaklaşım kriterler benim kod

// Non-recurive Quicksort for an array of Person objects
// adapted from http://www.algorithmist.com/index.php/Quicksort_non-recursive.php
function quickSort( &$array )
{
 $cur = 1;
 $stack[1]['l'] = 0;
 $stack[1]['r'] = count($array)-1;

 do
 {
  $l = $stack[$cur]['l'];
  $r = $stack[$cur]['r'];
  $cur--;

  do
  {
   $i = $l;
   $j = $r;
   $tmp = $array[(int)( ($l+$r)/2 )];

   // partion the array in two parts.
   // left from $tmp are with smaller values,
   // right from $tmp are with bigger ones
   do
   {
    while( $array[$i]->age < $tmp->age )
     $i++;

    while( $tmp->age < $array[$j]->age )
     $j--;

    // swap elements from the two sides
    if( $i <= $j)
    {
     $w = $array[$i];
     $array[$i] = $array[$j];
     $array[$j] = $w;

     $i++;
     $j--;
    }

   }while( $i <= $j );

 if( $i < $r )
   {
    $cur++;
    $stack[$cur]['l'] = $i;
    $stack[$cur]['r'] = $r;
   }
   $r = $j;

  }while( $l < $r );

 }while( $cur != 0 );


}


// usort() comparison function for Person objects
function personSort( $a, $b ) {
    return $a->age == $b->age ? 0 : ( $a->age > $b->age ) ? 1 : -1;
}


// simple person object    
class Person {
  var $age;
  function __construct($age) {
    $this->age = $age;
  }
}

//---------test internal usort() on 15000 Person objects------

srand(1);
$people=array();
for ($x=0; $x<15000; $x++)
{
     $people[]=new Person(rand(1,100));
}


$start=microtime(true);
usort( $people, 'personSort' );
$total=microtime(true)-$start;

echo "usort took $total\n";


//---------test custom quicksort on 15000 Person objects------

srand(1);
$people=array();
for ($x=0; $x<15000; $x++)
{
     $people[]=new Person(rand(1,100));
}


$start=microtime(true);
quickSort( $people );
$total=microtime(true)-$start;

echo "quickSort took $total\n";

Ilginç bir öneri sınıfa __toString yöntemi eklemek ve () sıralama kullanmak olduğunu, bu yüzden ben de o denedi. Sorun bu aslında bir dize yerine sayısal sıralama yapmanın yan etkisi vardır sihirli yöntemini çağırmak için almak sıralamak için ikinci parametre olarak SORT_STRING geçmelidir olduğunu. Bunu engellemek için, sıraladığınız düzgün yapmak için ped sıfır ile numaraları gerekir. Net sonuç, bu usort ve özel QuickSort hem daha yavaş oldu

sort 10000 items took      1.76266698837
usort 10000 items took     1.08757710457
quickSort 10000 items took 0.320873022079

İşte __ toString kullanarak sort () () için kod:

$size=10000;

class Person {
  var $age;
  function __construct($age) {
    $this->age = $age;
    $this->sortable=sprintf("%03d", $age);
  }


  public function __toString()
  {
     return $this->sortable;
  }
}

srand(1);
$people=array();
for ($x=0; $x<$size; $x++)
{
     $people[]=new Person(rand(1,100));
}


$start=microtime(true);
sort( $people, SORT_STRING);
$total=microtime(true)-$start;

echo "sort($size) took $total\n"

Belirli senaryoda, size dizideki öğeleri karşılaştırmak için kendi işlevini tanımlamak usort () fonksiyonu kullanarak sıralayabilirsiniz.

<?php

class Person {
  var $age;
  function __construct($age) {
    $this->age = $age;
  }
}

function personSort( $a, $b ) {
    return $a->age == $b->age ? 0 : ( $a->age > $b->age ) ? 1 : -1;
}

$person1 = new Person(14);
$person2 = new Person(5);
$person3 = new Person(32);
$person4 = new Person(150);
$person5 = new Person(39);
$people = array($person1, $person2, $person3, $person4, $person5);

print_r( $people );

usort( $people, 'personSort' );

print_r( $people );

Kullanmak usort or a heap. Olabilir ya

 class SortPeopleByAge extends SplMaxHeap
  {
      function compare($person1, $person2)
      {
          return $person1->age - $person2->age;
      }
  }

  $people = array(new Person(30), new Person(22), new Person(40));  
  $sorter = new SortPeopleByAge;
  array_map(array($sorter, 'insert'), $people);
  print_r(iterator_to_array($sorter)); // people sorted from 40 to 22

Bir Heap amacı her zaman sıralı bir koleksiyonu var ve değiştirmek için değil unutmayın usort. For large collections (1000+), a heap will be faster and less memory intensive though.

Yığınlar gibi diğer sıralama fonksiyonları için geri aramalar için kendi karşılaştırma işlevini kullanmak mümkün ediliyor olmasının bir yararı usort. Sadece karşılaştırma için sırası tersine, yani bir Heap ile yapılan herhangi bir karşılaştırma usort içinde ters sırayla neden olacağını hatırlamak zorunda.

// using $people array and $sorter
usort($people, array($sorter, 'compare'));
print_r($people); // people sorted from 22 to 40

usort Ne yapacağız orta koleksiyonları küçük için gayet sonunda bir kez sıralama. Tabii ki, kullanmak için bir yığın zorunda kalmazsınız usort. Sadece de sıralama için başka herhangi bir geçerli geri arama ekleyebilirsiniz.

Çirkin (Ve ben benchmarked değil) olurdu, çünkü ben sizin örnekte benim çözüm tavsiye yok, ama işe yarıyor .... Ve ihtiyaca bağlı olarak, bu yardımcı olabilir. :)

class Person
{
  public $age;

  function __construct($age)
  {
    $this->age = $age;
  }

  public function __toString()
  {
    return $this->age;
  }
}

$person1 = new Person(14);
$person2 = new Person(5);

$persons = array($person1, $person2);
asort($persons);

Sadece usort gerçek sıralamayı yapmak gibi bir şey kullanmak, daha sonra özel bir karşılaştırma işlevi yazmak gerekir. Üye değişkeni ise aşağıdaki gibi Örneğin, myVar, bunu sıralamak:

function cmp($a, $b)
{
    if ($a->myVar == $b->myVar) {
        return 0;
    }
    return ($a->myVar < $b->myVar) ? -1 : 1;
}

usort($myArray, "cmp");

Ben aşağıdaki yaklaşımı ile gitti: Ben ksort kullanarak o tür onlar Dizi anahtarları, dizi için anahtar olarak özelliğini kullanarak ilişkilendirilebilir bir dizi oluşturmak işlevi içinde, sonra nesneleri dizisi alır bir işlevi oluşturuldu:

class Person {
    var $age;
    function __construct($age) {
      $this->age = $age;
    }
}

function sortPerson($persons = Array()){
    foreach($persons as $person){
        $sorted[$person->age] = $person;
    }
    ksort($sorted);
    return array_values($sorted);
}

$person1 = new Person(14);
$person2 = new Person(5);

$persons = array($person1, $person2);
$person = sortPerson($persons);

echo $person[0]->age."\n".$person[1]->age;
/* Output:
5
14
*/

İşte değerleri 0 ... 256 için stable Radix Sort uygulaması bulunuyor:

function radixsort(&$a)
{
    $n = count($a);
    $partition = array();
    for ($slot = 0; $slot < 256; ++$slot) {
        $partition[] = array();
    }
    for ($i = 0; $i < $n; ++$i) {
        $partition[$a[$i]->age & 0xFF][] = &$a[$i];
    } 
    $i = 0;
    for ($slot = 0; $slot < 256; ++$slot) {
        for ($j = 0, $n = count($partition[$slot]); $j < $n; ++$j) {
            $a[$i++] = &$partition[$slot][$j];
        }
    }
}

Bu sadece O (n) Radix Sıralama beri olmayan bir karşılaştırma sıralama algoritması maliyeti.

Söz konusu tüm üye değişkenleri farklı olması garantili ise, bu değerlere endeksli yeni bir koleksiyon oluşturmak için basit ve hızlı olacak ve daha sonra ksort ver:

 foreach($obj_list as $obj)
    $map[$obj->some_var] = $obj;
 ksort($map);
 /// $map now contains the sorted list

Yinelenen değerler varsa, hala usort sort diziler diziler ilk skaler üyesinin değerine göre sıralanmış olduğu bir az bilinen bir özelliği kullanarak önleyebilirsiniz.

 foreach($obj_list as $obj)
    $map[] = array($obj->some_var, $obj);
 sort($map); // sorts $map by the value of ->some_var

Ben bu hala 10000000 kat daha hızlı usort daha olacak sanırım

usort() veya uasort() /* to maintain index association if you were using an associative array */

Evet. Eğer kişi object spl ArrayObject uygularsanız, tüm normal php dizi işlevler onunla düzgün çalışacaktır.

Bir gözlem veri kaynağı bir veritabanı ise, muhtemelen PHP içinde olacağını daha SQL kullanarak sıralamak için daha hızlı olmasıdır. Veri kaynağı bir CSV veya XML dosyası ise elbette bu tartışılır.

Usort deneyin: http://www.php.net/manual/en/function.usort.php

Örnek:

<?php
function cmp($obja, $objb)
{
    $a = $obja->sortField;
    $b = $objb->sortField;
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$a = array( /* your objects */ );

usort($a, "cmp");

?>

Ben sadece bu kodlanmış. Bu sayısız fonksiyon çağrıları dayanmaz gibi usort daha hızlı olmalıdır.

function sortByProp($array, $propName, $reverse = false)
{
    $sorted = [];

    foreach ($array as $item)
    {
        $sorted[$item->$propName][] = $item;
    }

    if ($reverse) krsort($sorted); else ksort($sorted);
    $result = [];

    foreach ($sorted as $subArray) foreach ($subArray as $item)
    {
        $result[] = $item;
    }

    return $result;
}

Kullanımı:

$sorted = sortByProp($people, 'age');

Oh, ve ksort kullanır ama çok $people aynı $age vardır bile çalışır.