Source for file RSArrayUtil.php

Documentation is available at RSArrayUtil.php


1 <?php
2 // RSArrayUtil: An Array Utility Class
3 // Copyright (C) 2003 Lukas Feiler
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 /**Contains the class RSArrayUtil (and other helper classes).
20 *
21 * @package RSArrayUtil
22 * @author Lukas Feiler <lukas.feiler@chello.at>
23 * @copyright Lukas Feiler 2003
24 * @filesource
25 */
26
27 /**Some methods accept an argument to change the comparison operator. Pass this constant to force strict comparison using '==='.
28 * @see RSArrayUtil::allTrue
29 */
30 define('RSARRAYUTIL_STRICT_COMPARISONS', true);
31
32 /**Some methods accept an argument to change the comparison operator. Pass this constant to force 'lazy' comparison using '=='.
33 * @see RSArrayUtil::allTrue
34 */
35 define('RSARRAYUTIL_LAZY_COMPARISONS', false);
36
37 /**The comparator class RSArrayComparator accepts a constructor argument that defines if it
38 * should ignore the order of the elements. RSArrayUtil::getAllCombinations does the same - but
39 * in contrast to RSArrayComparator ignoring the order is the default.
40 * @see RSArrayComparator
41 * @see RSArrayUtil::getAllCombinations
42 */
43 define('RSARRAYUTIL_IGNORE_ORDER', true);
44
45 /**The comparator class RSArrayComparator accepts a constructor argument that defines if it
46 * should ignore the order of the elements. RSArrayUtil::getAllCombinations does the same - but
47 * in contrast to RSArrayComparator ignoring the order is the default.
48 * @see RSArrayComparator
49 * @see RSArrayUtil::getAllCombinations
50 */
51 define('RSARRAYUTIL_RESPECT_ORDER', false);
52
53 /**A Utility Class for Simple and Advanced Arrays Operations.
54 *
55 * General argument order for all methods:
56 * The array to operate on is allways the first argument.
57 *
58 * @author Lukas Feiler <lukas.feiler@chello.at>
59 * @version 0.1.9
60 * @copyright Lukas Feiler 2003
61 * @package RSArrayUtil
62 * @static
63 */
64 class RSArrayUtil {
65
66 /**Returns a literal representation of an array.
67 *
68 * This method is very flexible because it allows the programmer to specify
69 * the format of the key value pairs and the seperation character between these pairs.
70 * If the value is an object and has a toString method, that method is called to express the value of this array element.
71 *
72 * @param Array $array The array to operate on.
73 * @param String $elementSeperator The String to insert between the array elements. The default is an HTML break <br>.
74 * @param String $format The output format of one key value pair (on array element). Can be any string. %key is the placholder for the key and %val is the placeholder for the value. The default is "%key:%val".
75 * @return String A literal representation of the array passed as first argument.
76 */
77 function toString($array, $elementSeperator = "<br>", $format = "%key:%val")
78 {
79 $ret = "";
80 reset($array);
81 while (list($key,$val) = each($array)) {
82 if (method_exists($val,"toString")) {
83 $val = $val->toString();
84 }
85 if ($ret != "") {
86 $ret .= $elementSeperator;
87 }
88 $ret .= str_replace("%val", $val, str_replace("%key", $key,$format));
89 }
90 reset($array);
91 return $ret;
92 }
93
94 /**Returns true if all elements of the array passed as argument evaluate true.
95 *
96 * @param Array $array The array to validate.
97 * @param boolean $strictComparison Whether to perfor a comparison using === or ==. The default is ===.
98 * @return boolean True if all elements are true.
99 */
100 function allTrue($array, $strictComparison = true)
101 {
102 if (!is_array($array)) {
103 return false;
104 }
105 while (list($key, $val) = each($array)) {
106 if ($strictComparison) {
107 if($val !== true) {
108 return false;
109 }
110 } else {
111 if($val != true) {
112 return false;
113 }
114 }
115 }
116 return true;
117 }
118
119
120 /**Returns an array containing all elements from the multidimensional array that where referenced by a key equal to $key.
121 *
122 * Example:
123 * <code>
124 * $array = array(
125 * array(
126 * 'a' => 'zero',
127 * 'b' => 'one',
128 * 'c' => 'two'
129 * ),
130 * array(
131 * 'a' => 'one',
132 * 'b' => 'two',
133 * 'c' => 'three'
134 * ),
135 * array(
136 * 'a' => 'two',
137 * 'b' => 'three',
138 * 'c' => 'four'
139 * )
140 * );
141 * $aArray = RSArrayUtil::getElementsFromMultiDimArrayByKey($array, 'a');
142 * //will echo '0:zero, 1:one, 2:two'
143 * echo RSArrayUtil::toString($aArray, ', ', '%key:%val');
144 * </code>
145 * @param Array $array A multidimensional array.
146 * @param mixed $key The key to search for.
147 * @return Array An array containing all found elements.
148 */
149 function getElementsFromMultiDimArrayByKey($array, $key){
150 $ret = array();
151 reset($array);
152 while (list($key2,$element) = each($array)) {
153 if (isset($element[$key])) {
154 $ret[] =& $element[$key];
155 }
156 }
157 return $ret;
158 }
159
160 /**Returns the array passed as first argument with the element passed as second argument added.
161 *
162 * This method can be used in two ways. The first one is to pass the array by value and save the return type of addElement:
163 * <code>
164 * $array = RSArrayUtil::addElement($array,$element);
165 * </code>
166 *
167 * The second way is to pass the array by reference (an not care about the return value):
168 * <code>
169 * RSArrayUtil::addElement($array, $element);
170 * </code>
171 *
172 * Both ways have the same result!
173 *
174 * @param Array $array The array, the element passed as second argument should be added to.
175 * @param mixed $element The element to add. Can be any type.
176 */
177 function &addElement(&$array, &$element)
178 {
179 $array[] =& $element;
180 return $array;
181 }
182
183 /**Returns the array passed as first argument with the elements contained in the array passed as second argument added.
184 *
185 * This method can be used in two ways. The first one is to pass the first array by value and save the return type of addElements:
186 * <code>
187 * $array = RSArrayUtil::addElements($array,$array2);
188 * </code>
189 *
190 * The second way is to pass the first array by reference (an not care about the return value):
191 * <code>
192 * RSArrayUtil::addElements($array, $array2);
193 * </code>
194 *
195 * Both ways have the same result!
196 * Please note that the keys of the added elements are not preserved.
197 *
198 * @param Array $array The array, the element passed as second argument should be added to.
199 * @param mixed $element The element to add. Can be any type.
200 */
201 function &addElements(&$array, &$elements)
202 {
203 while (list($key, $element) = each($elements)) {
204 RSArrayUtil::addElement($array, $elements[$key]);
205 }
206 return $array;
207 }
208
209 /**Returns true if the the array passed as first argument contains the element passed as second argument.
210 *
211 * The comparison is done by {@link PHP_MANUAL#in_array} without type checking by default.
212 * An other comparison can be forced by passing an instance of the class RSComparator or a class that extends it.
213 * Simple example:
214 * <code>
215 * $array = array('a', 'b', 'c');
216 * if (RSArrayUtil::hasElement($array, 'b')) {
217 * echo 'b is contained in the array';
218 * }
219 * </code>
220 * Complex example using a comperator:
221 * <code>
222 * class TestElement {
223 * var $hash = null;
224 * function TestElement($hash)
225 * {
226 * $this->hash = $hash;
227 * }
228 * function hashCode()
229 * {
230 * return $this->hash;
231 * }
232 * }
233 * $array = array(new TestElement('a'), new TestElement('b'), new TestElement('c'));
234 * if (RSArrayUtil::hasElement($array, new TestElement('c'), new RSComparator())) {
235 * echo 'c is contained in the array';
236 * }
237 * </code>
238 * @see RSComparator
239 *
240 * @param Array $array The array to operate on.
241 * @param mixed $element The element to search for in the array passed as first argument. Can by any type.
242 * @param RSComparator $comparator An instance of the class RSComparator or a class that extends it. This argument is optional.
243 * @return boolean True if the array passed as first argument contains the element passed as second argument.
244 */
245 function hasElement($array, $element, $comparator = false)
246 {
247 if (method_exists($comparator, 'compare')) {
248 reset($array);
249 while (list($key, $val) = each($array)) {
250 if ($comparator->compare($val, $element) === 0) {
251 return true;
252 }
253 }
254 return false;
255 } else {
256 return in_array($element, $array);
257 }
258 }
259
260 /**Returns true if the the array passed as first argument contains all elements of the array passed as second argument.
261 *
262 * The comparison is handled by hasElement which uses {@link PHP_MANUAL#in_array} without type checking by default.
263 * An other comparison can be forced by passing an instance of the class RSComparator or a class that extends it.
264 * @see hasElement
265 * @see RSComparator
266 *
267 * @param Array $array The array to search in.
268 * @param Array $elements An array of elements to search for in the array passed as first argument.
269 * @param RSComparator $comparator An instance of the class RSComparator or a class that extends it. This argument is optional.
270 * @return boolean True if the array passed as first argument contains all elements of the array passed as second argument.
271 */
272 function hasElements($array, $elements, $comparator = false)
273 {
274 while (list($key, $val) = each($elements)) {
275 if (!RSArrayUtil::hasElement($array, $val, $comparator)) {
276 return false;
277 }
278 }
279 return true;
280 }
281
282 /**Returns the number of occurrences of a value in an array.
283 *
284 * The comparison is done by the === operator.
285 * An other comparison can be forced by passing an instance of the class RSComparator or a class that extends it.
286 * @see RSComparator
287 *
288 * @param Array $array The array to operate on.
289 * @param mixed $element The element to search for in the array passed as first argument. Can by any type.
290 * @param RSComparator $comparator An instance of the class RSComparator or a class that extends it. This argument is optional.
291 * @return int The number or occurrences.
292 */
293 function countElement($array, $element, $comparator = false)
294 {
295 $count = 0;
296 while (list($key, $val) = each($array)) {
297 if (method_exists($comparator, 'compare')) {
298 if ($comparator->compare($val, $element) === 0) {
299 $count++;
300 }
301 } else {
302 if ($val === $element) {
303 $count++;
304 }
305 }
306 }
307 return $count;
308 }
309
310 /**Removes all duplicates from an array.
311 *
312 * A new array with all duplicates removed is returned.
313 * The comparison is handled by hasElement which uses {@link PHP_MANUAL#in_array} without type checking by default.
314 * An other comparison can be forced by passing an instance of the class RSComparator or a class that extends it.
315 * This method iterates over the array passed as argument and copies all elements to the new array that are not already contained in it.
316 * @see RSComparator
317 * @see hasElement
318 *
319 * @param Array $array The array to operate on.
320 * @param RSComparator $comparator An instance of the class RSComparator or a class that extends it. This argument is optional.
321 * @return Array A new array containing all unique elements of the array passed as first argument.
322 */
323 function removeDuplicates(&$array, $comparator = false)
324 {
325 reset($array);
326 $ret = array();
327 while (list($key, $val) = each($array)) {
328 if (!RSArrayUtil::hasElement($ret, $val, $comparator)) {
329 $ret[] = $val;
330 }
331 }
332 $array =& $ret;
333 return $ret;
334 }
335
336 /**Returns all possible combinations of the elements of an array.
337 *
338 * If the array would contain three elements, holding the integers 1, 2 and 3
339 * the returned array of combinations would have a length of 39 and start off
340 * like this (if configured not to ignore the order and to allow duplicates):
341 * <ul>
342 * 1
343 * 1,1
344 * 1,1,1
345 * 1,1,2
346 * 1,1,3
347 * 1,2
348 * 1,2,1
349 * </ul>
350 * @param Array $elements Array that contains all possible elements.
351 * @param mixed $comparator An instance of the class RSComparator or
352 * a class that extends it.
353 * @param boolean $ignoreOrder Whether to ignore the order of the elements
354 * when checking if such a combination already exists.
355 * This argument is optional. The default is the true.
356 * @param boolean $allowMultipleOccurrencesInCombination Whether to allow
357 * multiple occurrences in one combination. This argument is optional.
358 * The default is false.
359 * @param Array $combinations An array of combinations already computed.
360 * This argument is used internally because this method is recursive.
361 * @param Array $lastCombination Do not use this argument! It is only
362 * internally needed because this method is recursive. This argument is
363 * optional. The default is array().
364 * @param mixed $maxLength The maximum number of elements in a combination.
365 * If set to false the maximum is the number of possible elements passed
366 * in as first argument. This argument is optional. The default is false.
367 */
368 function getAllCombinations($elements, $comparator, $ignoreOrder = true, $allowMultipleOccurrencesInCombination = false, $combinations = array(), $maxLength = false, $lastCombination = array())
369 {
370 static $myCounter;
371 //echo "getAllCombinations " . ($myCounter++) . "<br>";
372 if ($maxLength === false) {
373 $maxLength = count($elements);
374 }
375 for ($i = 0; $i < count($elements); $i++) {
376 $newCombination = $lastCombination;
377
378 if (!$allowMultipleOccurrencesInCombination && RSArrayUtil::hasElement($newCombination, $elements[$i], $comparator)) {
379 continue;
380 }
381
382 $newCombination[] = $elements[$i];
383 if (RSArrayUtil::hasElement($combinations, $newCombination, new RSArrayComparator($comparator, $ignoreOrder))) {
384 continue;
385 }
386
387 $combinations[] = $newCombination;
388 if (count($newCombination) >= $maxLength) {
389 continue;
390 }
391 $combinations = RSArrayUtil::getAllCombinations($elements, $comparator, $ignoreOrder, $allowMultipleOccurrencesInCombination, $combinations, $maxLength, $newCombination);
392 }
393 return $combinations;
394 }
395 }
396
397 /**Base class for all implementations of comparators.
398 *
399 * A comparator does nothing more than to implement/inherit the method compare that takes two arguments and compares them.
400 * If the two arguments are equal the value 0 is returned - owtherwise a non-zero value is returned.
401 * @see compare
402 *
403 * @author Lukas Feiler <lukas.feiler@chello.at>
404 * @version 0.1.9
405 * @copyright Lukas Feiler 2003
406 * @package RSArrayUtil
407 */
408 class RSComparator{
409 /**Returns 0 if objects passed as first and second argument are equal. They have to implement the method hashCode.
410 *
411 * If the return value of the method call hashCode on the object passed as first agrument is equal (==) to the
412 * return value of the same methodcall on the object passed as second argument 0 is returned; otherwise -1.
413 *
414 * @param Object $o1 An object that implements a method called hashCode.
415 * @param Object $o2 An object that implements a method called hashCode.
416 */
417 function compare($o1, $o2)
418 {
419 if ($o1->hashCode() == $o2->hashCode()) {
420 return 0;
421 }
422 return -1;
423 }
424 }
425
426 /**A comparator for arrays.
427 *
428 * @see RSArrayUtil
429 *
430 * @author Lukas Feiler <lukas.feiler@chello.at>
431 * @version 0.1.9
432 * @copyright Lukas Feiler 2003
433 * @package RSArrayUtil
434 */
435 class RSArrayComparator extends RSComparator{
436 /**Holds the boolean value false or an instance of the cass RSComperator or a class that extends it. Gets initialized in the construtor method.
437 * @var mixed
438 * @see RSArrayComparator
439 * @access private
440 */
441 var $_subComparator = false;
442
443 /**Whether to ignore the order of the elements. Gets set inside the constructor method.
444 * @var boolean
445 */
446 var $_ignoreOrder = false;
447
448 /**Constructor method that initializes the private field _subComparator.
449 *
450 * @param RSComperator $subComparator An instance of the cass RSComperator or a class that extends it. This argument is optional.
451 */
452 function RSArrayComparator($subComparator = false, $ignoreOrder = false)
453 {
454 if (!method_exists($subComparator, 'compare')) {
455 $subComparator = new RSPrimitiveComparator();
456 }
457 $this->_subComparator = $subComparator;
458 $this->_ignoreOrder = $ignoreOrder;
459 }
460
461 /**Returns 0 if both arrays contain the same elements.
462 *
463 * Note that the order of the elements is NOT ignored.
464 */
465 function compare($array1, $array2)
466 {
467 if (count($array1) !== count($array2)) {
468 return -1;
469 }
470
471 if ($this->_ignoreOrder) {
472 while (list($key, $val) = each($array1)) {
473 //echo RSArrayUtil::countElement($array1, $array1[$key], $this->_subComparator) . " !== " . RSArrayUtil::countElement($array2, $array1[$key], $this->_subComparator) . "\n";
474 if (
475 RSArrayUtil::countElement($array1, $array1[$key], $this->_subComparator)
476 !==
477 RSArrayUtil::countElement($array2, $array1[$key], $this->_subComparator)
478 ) {
479 return -1;
480 }
481 }
482 return 0;
483 } else {
484 uasort($array1, array ($this->_subComparator, "compare"));
485 uasort($array2, array ($this->_subComparator, "compare"));
486
487 //copy elements to new arrays - just in case one of them is an associative array
488 while (list($key, $val) = each($array1)) {
489 $array1Copy[] = $array1[$key];
490 }
491 while (list($key, $val) = each($array2)) {
492 $array2Copy[] = $array2[$key];
493 }
494
495
496 while (list($key, $val) = each($array1Copy)) {
497 if ($this->_subComparator->compare($array1Copy[$key], $array2Copy[$key]) !== 0) {
498 return -1;
499 }
500 }
501 return 0;
502 }
503 }
504 }
505
506
507 /**A Comparator for comparing objects by the return values of their toString methods.
508 *
509 * @author Lukas Feiler <lukas.feiler@chello.at>
510 * @version 0.1.9
511 * @copyright Lukas Feiler 2003
512 * @package RSArrayUtil
513 */
514 class RSToStringComparator extends RSComparator{
515 /**Returns 0 if both objects implement the method toString and the return values of both methods is equal (==). Otherwise -1 is returned.
516 */
517 function compare($o1, $o2)
518 {
519 if (method_exists($o1, "toString") && method_exists($o2, "toString")) {
520 if ($o1->toString() == $o2->toString()) {
521 return 0;
522 }
523 return -1;
524 }
525 return -1;
526 }
527 }
528
529 /**A Comparator for comparing primitives.
530 *
531 * @author Lukas Feiler <lukas.feiler@chello.at>
532 * @version 0.1.9
533 * @copyright Lukas Feiler 2003
534 * @package RSArrayUtil
535 */
536 class RSPrimitiveComparator extends RSComparator{
537
538 /**Returns 0 if the two arguments are equal (===), otherwise -1.
539 */
540 function compare($o1, $o2)
541 {
542 if ($o1 === $o2) {
543 return 0;
544 }
545 return -1;
546 }
547 }
548 ?>

Documentation generated on Mon, 8 Dec 2003 13:10:38 +0100 by phpDocumentor 1.2.3