/
SortIterator.php
156 lines (139 loc) · 3.77 KB
/
SortIterator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Collection\Iterator;
use Cake\Collection\ExtractTrait;
use SplHeap;
/**
* An iterator that will return the passed items in order. The order is given by
* the value returned in a callback function that maps each of the elements.
*
* ###Example:
*
* {{{
* $items = [$user1, $user2, $user3];
* $sorted = new SortIterator($items, function($user) {
* return $user->age;
* });
*
* // output all user name order by their age in descending order
* foreach ($sorted as $user) {
* echo $user->name;
* }
* }}}
*
* This iterator does not preserve the keys passed in the original elements.
*/
class SortIterator extends SplHeap {
use ExtractTrait;
/**
* Original items passed to this iterator
*
* @var array|\Traversable
*/
protected $_items;
/**
* The callback used to extract the column or property from the elements
*
* @var callable
*/
protected $_callback;
/**
* The direction in which the elements should be sorted. The constants
* `SORT_ASC` and `SORT_DESC` are the accepted values
*
* @var string
*/
protected $_dir;
/**
* The type of sort comparison to perform.
*
* @var string
*/
protected $_type;
/**
* Wraps this iterator around the passed items so when iterated they are returned
* in order.
*
* The callback will receive as first argument each of the elements in $items,
* the value returned in the callback will be used as the value for sorting such
* element. Please not that the callback function could be called more than once
* per element.
*
* @param array|\Traversable $items The values to sort
* @param callable|string $callback A function used to return the actual value to
* be compared. It can also be a string representing the path to use to fetch a
* column or property in each element
* @param integer $dir either SORT_DESC or SORT_ASC
* @param integer $type the type of comparison to perform, either SORT_STRING
* SORT_NUMERIC or SORT_NATURAL
* @return void
*/
public function __construct($items, $c, $dir = SORT_DESC, $type = SORT_NUMERIC) {
$this->_items = $items;
$this->_dir = $dir;
$this->_type = $type;
$this->_callback = $this->_propertyExtractor($c);
}
/**
* The comparison function used to sort the elements
*
* @param mixed $a an element in the list
* @param mixed $b an element in the list
* @return integer
*/
public function compare($a, $b) {
if ($this->_dir === SORT_ASC) {
list($a, $b) = [$b, $a];
}
$callback = $this->_callback;
$a = $callback($a);
$b = $callback($b);
if ($this->_type === SORT_NUMERIC) {
return $a - $b;
}
if ($this->_type === SORT_NATURAL) {
return strnatcmp($a, $b);
}
if ($this->_type === SORT_STRING) {
return strcmp($a, $b);
}
return strcoll($a, $b);
}
/**
* Returns the top of the heap. Rewinds the iterator if the heap is empty.
*
* @return mixed
*/
public function top() {
if ($this->isEmpty()) {
$this->rewind();
}
if ($this->isEmpty()) {
return null;
}
return parent::top();
}
/**
* SplHeap removes elements upon iteration. Implementing rewind so that
* this iterator can be reused, at least at a cost.
*
* @return void
*/
public function rewind() {
foreach ($this->_items as $item) {
$this->insert($item);
}
}
}