From 87e7366d4d9d3de79772b851f8772a4011d6227d Mon Sep 17 00:00:00 2001 From: javalizard <> Date: Sat, 25 Jun 2011 04:56:12 +0000 Subject: Added two methods for getting array elements out of TPriorityList above and below a specific priority, with inclusion. Added TPriorityMap and test cases. --- .gitattributes | 2 + framework/Collections/TPriorityList.php | 323 ++++++-------- framework/Collections/TPriorityMap.php | 606 +++++++++++++++++++++++++++ tests/unit/Collections/TPriorityListTest.php | 71 ++++ tests/unit/Collections/TPriorityMapTest.php | 490 ++++++++++++++++++++++ 5 files changed, 1307 insertions(+), 185 deletions(-) create mode 100644 framework/Collections/TPriorityMap.php create mode 100644 tests/unit/Collections/TPriorityMapTest.php diff --git a/.gitattributes b/.gitattributes index 37006a6b..f76c026f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2370,6 +2370,7 @@ framework/Collections/TMap.php -text framework/Collections/TPagedDataSource.php -text framework/Collections/TPagedList.php -text framework/Collections/TPriorityList.php -text +framework/Collections/TPriorityMap.php -text framework/Collections/TQueue.php -text framework/Collections/TStack.php -text framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php -text @@ -3761,6 +3762,7 @@ tests/unit/Collections/TMapTest.php -text tests/unit/Collections/TPagedDataSourceTest.php -text tests/unit/Collections/TPagedListTest.php -text tests/unit/Collections/TPriorityListTest.php -text +tests/unit/Collections/TPriorityMapTest.php -text tests/unit/Collections/TQueueTest.php -text tests/unit/Collections/TStackTest.php -text tests/unit/Data/DataGateway/AllTests.php -text diff --git a/framework/Collections/TPriorityList.php b/framework/Collections/TPriorityList.php index d6fc807c..e952099c 100644 --- a/framework/Collections/TPriorityList.php +++ b/framework/Collections/TPriorityList.php @@ -119,9 +119,9 @@ class TPriorityList extends TList */ public function getPriorityCount($priority=null) { - if($priority === null) - $priority = $this->getDefaultPriority(); - $priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p); + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if(!isset($this->_d[$priority]) || !is_array($this->_d[$priority])) return false; @@ -142,7 +142,7 @@ class TPriorityList extends TList */ protected function setDefaultPriority($value) { - $this->_dp = (string)round(TPropertyValue::ensureFloat($value), $this->_p); + $this->_dp=(string)round(TPropertyValue::ensureFloat($value),$this->_p); } /** @@ -169,7 +169,7 @@ class TPriorityList extends TList */ public function getIterator() { - return new ArrayIterator( $this->flattenPriorities() ); + return new ArrayIterator($this->flattenPriorities()); } /** @@ -188,8 +188,8 @@ class TPriorityList extends TList */ protected function sortPriorities() { if(!$this->_o) { - ksort($this->_d, SORT_NUMERIC); - $this->_o = true; + ksort($this->_d,SORT_NUMERIC); + $this->_o=true; } } @@ -202,9 +202,9 @@ class TPriorityList extends TList return $this->_fd; $this->sortPriorities(); - $this->_fd = array(); + $this->_fd=array(); foreach($this->_d as $priority => $itemsatpriority) - $this->_fd = array_merge($this->_fd, $itemsatpriority); + $this->_fd=array_merge($this->_fd,$itemsatpriority); return $this->_fd; } @@ -218,8 +218,8 @@ class TPriorityList extends TList */ public function itemAt($index) { - if($index>=0 && $index<$this->getCount()) { - $arr = $this->flattenPriorities(); + if($index>=0&&$index<$this->getCount()) { + $arr=$this->flattenPriorities(); return $arr[$index]; } else throw new TInvalidDataValueException('list_index_invalid',$index); @@ -232,11 +232,11 @@ class TPriorityList extends TList */ public function itemsAtPriority($priority=null) { - if($priority === null) - $priority = $this->getDefaultPriority(); - $priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p); + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); - return isset($this->_d[$priority]) ? $this->_d[$priority] : null; + return isset($this->_d[$priority])?$this->_d[$priority]:null; } /** @@ -247,12 +247,12 @@ class TPriorityList extends TList */ public function itemAtIndexInPriority($index,$priority=null) { - if($priority === null) - $priority = $this->getDefaultPriority(); - $priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p); + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); - return !isset($this->_d[$priority]) ? false : ( - isset($this->_d[$priority][$index]) ? $this->_d[$priority][$index] : false + return !isset($this->_d[$priority])?false:( + isset($this->_d[$priority][$index])?$this->_d[$priority][$index]:false ); } @@ -264,7 +264,7 @@ class TPriorityList extends TList * @return int the index within the flattened array * @throws TInvalidOperationException if the map is read-only */ - public function add($item, $priority=null) + public function add($item,$priority=null) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); @@ -280,7 +280,7 @@ class TPriorityList extends TList * @throws TInvalidDataValueException If the index specified exceeds the bound * @throws TInvalidOperationException if the list is read-only */ - public function insertAt($index, $item) + public function insertAt($index,$item) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); @@ -313,18 +313,18 @@ class TPriorityList extends TList if($preserveCache) { $this->sortPriorities(); $cc=0; - foreach($this->_d as $prioritykey => $items) - if($prioritykey >= $priority) + foreach($this->_d as $prioritykey=>$items) + if($prioritykey>=$priority) break; else $cc+=count($items); - if($index === false && isset($this->_d[$priority])) { - $c = count($this->_d[$priority]); - $c += $cc; + if($index===false&&isset($this->_d[$priority])) { + $c=count($this->_d[$priority]); + $c+=$cc; $this->_d[$priority][]=$item; } else if(isset($this->_d[$priority])) { - $c = $index + $cc; + $c=$index+$cc; array_splice($this->_d[$priority],$index,0,array($item)); } else { $c = $cc; @@ -332,25 +332,25 @@ class TPriorityList extends TList $this->_d[$priority]=array($item); } - if($this->_fd && is_array($this->_fd)) // if there is a flattened array cache + if($this->_fd&&is_array($this->_fd)) // if there is a flattened array cache array_splice($this->_fd,$c,0,array($item)); } else { - $c = null; - if($index === false && isset($this->_d[$priority])) { - $cc = count($this->_d[$priority]); + $c=null; + if($index===false&&isset($this->_d[$priority])) { + $cc=count($this->_d[$priority]); $this->_d[$priority][]=$item; } else if(isset($this->_d[$priority])) { - $cc = $index; + $cc=$index; array_splice($this->_d[$priority],$index,0,array($item)); } else { - $cc = 0; - $this->_o = false; + $cc=0; + $this->_o=false; $this->_d[$priority]=array($item); } - if($this->_fd && is_array($this->_fd) && count($this->_d) == 1) + if($this->_fd&&is_array($this->_fd)&&count($this->_d)==1) array_splice($this->_fd,$cc,0,array($item)); else - $this->_fd = null; + $this->_fd=null; } $this->_c++; @@ -374,17 +374,17 @@ class TPriorityList extends TList if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); - if(($p=$this->priorityOf($item, true))!==false) + if(($p=$this->priorityOf($item,true))!==false) { - if($priority !== false) { - if($priority === null) - $priority = $this->getDefaultPriority(); - $priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p); + if($priority!==false) { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); - if($p[0] != $priority) + if($p[0]!=$priority) throw new TInvalidDataValueException('list_item_inexistent'); } - $this->removeAtIndexInPriority($p[1], $p[0]); + $this->removeAtIndexInPriority($p[1],$p[0]); return $p[2]; } else @@ -403,8 +403,8 @@ class TPriorityList extends TList if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); - if(($priority = $this->priorityAt($index, true))!==false) - return $this->removeAtIndexInPriority($priority[1], $priority[0]); + if(($priority=$this->priorityAt($index, true))!==false) + return $this->removeAtIndexInPriority($priority[1],$priority[0]); throw new TInvalidDataValueException('list_index_invalid',$index); } @@ -421,22 +421,22 @@ class TPriorityList extends TList if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); - if($priority === null) - $priority = $this->getDefaultPriority(); - $priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p); + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); - if(!isset($this->_d[$priority]) || $index < 0 || $index >= count($this->_d[$priority])) + if(!isset($this->_d[$priority])||$index<0||$index>=count($this->_d[$priority])) throw new TInvalidDataValueException('list_item_inexistent'); // $value is an array of elements removed, only one - $value = array_splice($this->_d[$priority],$index,1); - $value = $value[0]; + $value=array_splice($this->_d[$priority],$index,1); + $value=$value[0]; if(!count($this->_d[$priority])) unset($this->_d[$priority]); $this->_c--; - $this->_fd = null; + $this->_fd=null; return $value; } @@ -448,10 +448,10 @@ class TPriorityList extends TList if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); - $d = array_reverse($this->_d, true); - foreach($this->_d as $priority => $items) { - for($index = count($items) - 1; $index >= 0; $index--) - $this->removeAtIndexInPriority($index, $priority); + $d=array_reverse($this->_d,true); + foreach($this->_d as $priority=>$items) { + for($index=count($items)-1;$index>=0;$index--) + $this->removeAtIndexInPriority($index,$priority); unset($this->_d[$priority]); } } @@ -486,20 +486,18 @@ class TPriorityList extends TList * if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex, * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex] */ - public function priorityOf($item, $withindex = false) + public function priorityOf($item,$withindex = false) { - // this is to ensure priority order $this->sortPriorities(); $absindex = 0; - foreach($this->_d as $priority => $items) { + foreach($this->_d as $priority=>$items) { if(($index=array_search($item,$items,true))!==false) { - $absindex += $index; - return $withindex ? - array($priority, $index, $absindex, - 'priority' => $priority, 'index' => $index, 'absindex' => $absindex) : $priority; + $absindex+=$index; + return $withindex?array($priority,$index,$absindex, + 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; } else - $absindex += count($items); + $absindex+=count($items); } return false; @@ -514,21 +512,19 @@ class TPriorityList extends TList * if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex, * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex] */ - public function priorityAt($index, $withindex = false) + public function priorityAt($index,$withindex = false) { - if($index < 0 || $index >= $this->getCount()) + if($index<0||$index>=$this->getCount()) throw new TInvalidDataValueException('list_index_invalid',$index); - // this is to ensure priority order - $absindex = $index; + $absindex=$index; $this->sortPriorities(); - foreach($this->_d as $priority => $items) { - if($index >= ($c = count($items))) - $index -= $c; + foreach($this->_d as $priority=>$items) { + if($index>=($c=count($items))) + $index-=$c; else - return $withindex ? - array($priority, $index, $absindex, - 'priority' => $priority, 'index' => $index, 'absindex' => $absindex) : $priority; + return $withindex?array($priority,$index,$absindex, + 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; } return false; } @@ -546,10 +542,10 @@ class TPriorityList extends TList if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); - if(($priority = $this->priorityOf($indexitem, true)) === false) + if(($priority=$this->priorityOf($indexitem,true))===false) throw new TInvalidDataValueException('list_item_inexistent'); - $this->insertAtIndexInPriority($item, $priority[1], $priority[0]); + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); return $priority[2]; } @@ -591,6 +587,46 @@ class TPriorityList extends TList $this->sortPriorities(); return $this->_d; } + + /** + * Combines the map elements which have a priority below the parameter value + * @param numeric the cut-off priority. All items of priority less than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: false, not inclusive. + * @return array the array of priorities keys with values of arrays of items that are below a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayBelowPriority($priority,$inclusive=false) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) + break; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + /** + * Combines the map elements which have a priority above the parameter value + * @param numeric the cut-off priority. All items of priority greater than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: true, inclusive. + * @return array the array of priorities keys with values of arrays of items that are above a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayAbovePriority($priority,$inclusive=true) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) + continue; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } /** @@ -601,20 +637,20 @@ class TPriorityList extends TList */ public function copyFrom($data) { - if($data instanceof TPriorityList) { + if($data instanceof TPriorityList) + { if($this->getCount()>0) $this->clear(); - foreach($data->getPriorities() as $priority) { - foreach($data->itemsAtPriority($priority) as $index => $item) - $this->insertAtIndexInPriority($item, $index, $priority); + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $index=>$item) + $this->insertAtIndexInPriority($item,$index,$priority); } - - } else if(is_array($data) || $data instanceof Traversable) { + } else if(is_array($data)||$data instanceof Traversable) { if($this->getCount()>0) $this->clear(); foreach($data as $key=>$item) $this->add($item); - } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } @@ -629,16 +665,21 @@ class TPriorityList extends TList */ public function mergeWith($data) { - if($data instanceof TPriorityList) { - foreach($data->getPriorities() as $priority) { - foreach($data->itemsAtPriority($priority) as $index => $item) - $this->insertAtIndexInPriority($item, false, $priority); + if($data instanceof TPriorityList) + { + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $index=>$item) + $this->insertAtIndexInPriority($item,false,$priority); } - } else if(is_array($data) || $data instanceof Traversable) { + } + else if(is_array($data)||$data instanceof Traversable) + { foreach($data as $priority=>$item) $this->add($item); - } else if($data!==null) + } + else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } @@ -650,7 +691,7 @@ class TPriorityList extends TList */ public function offsetExists($offset) { - return ($offset>=0 && $offset<$this->getCount()); + return ($offset>=0&&$offset<$this->getCount()); } /** @@ -678,16 +719,16 @@ class TPriorityList extends TList */ public function offsetSet($offset,$item) { - if($offset === null) + if($offset===null) return $this->add($item); - if($offset === $this->getCount()) { - $priority = $this->priorityAt($offset-1, true); + if($offset===$this->getCount()) { + $priority=$this->priorityAt($offset-1,true); $priority[1]++; } else { - $priority = $this->priorityAt($offset, true); - $this->removeAtIndexInPriority($priority[1], $priority[0]); + $priority=$this->priorityAt($offset,true); + $this->removeAtIndexInPriority($priority[1],$priority[0]); } - $this->insertAtIndexInPriority($item, $priority[1], $priority[0]); + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); } /** @@ -700,91 +741,3 @@ class TPriorityList extends TList $this->removeAt($offset); } } - -/** - * TPriorityListIterator class - * - * TPriorityListIterator implements Iterator interface. - * - * TPriorityListIterator is used by TPriorityList. It allows TPriorityList to return a new iterator - * for traversing the items in the priority list. - * - * @deprecated Issue 264 : ArrayIterator should be used instead - * @author Brad Anderson - * @version $Id: TPriorityList.php 2541 2010-10-03 15:05:13Z javalizard $ - * @package System.Collections - * @since 3.2a - */ -class TPriorityListIterator implements Iterator -{ - /** - * @var array the data to be iterated through - */ - private $_d; - /** - * @var array list of keys in the map - */ - private $_keys; - /** - * @var mixed current key - */ - private $_key; - - /** - * Constructor. - * @param array the data to be iterated through - */ - public function __construct(&$data) - { - $this->_d=&$data; - $this->_keys=array_keys($data); - } - - /** - * Rewinds internal array pointer. - * This method is required by the interface Iterator. - */ - public function rewind() - { - $this->_key=reset($this->_keys); - } - - /** - * Returns the key of the current array element. - * This method is required by the interface Iterator. - * @return mixed the key of the current array element - */ - public function key() - { - return $this->_key; - } - - /** - * Returns the current array element. - * This method is required by the interface Iterator. - * @return mixed the current array element - */ - public function current() - { - return $this->_d[$this->_key]; - } - - /** - * Moves the internal pointer to the next array element. - * This method is required by the interface Iterator. - */ - public function next() - { - $this->_key=next($this->_keys); - } - - /** - * Returns whether there is an element at current position. - * This method is required by the interface Iterator. - * @return boolean - */ - public function valid() - { - return $this->_key!==false; - } -} diff --git a/framework/Collections/TPriorityMap.php b/framework/Collections/TPriorityMap.php new file mode 100644 index 00000000..6f8ae8bd --- /dev/null +++ b/framework/Collections/TPriorityMap.php @@ -0,0 +1,606 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2011 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPriorityMap.php 2817 2010-04-18 04:25:03Z javalizard $ + * @package System.Collections + */ + +/** + * TPriorityMap class + * + * TPriorityMap implements a collection that takes key-value pairs with + * a priority to allow key-value pairs to be ordered. This ordering is + * important when flattening the map. When flattening the map, if some + * key-value pairs are required to be before or after others, use this + * class to keep order to your map. + * + * You can access, add or remove an item with a key by using + * {@link itemAt}, {@link add}, and {@link remove}. These functions + * can optionally take a priority parameter to allow access to specific + * priorities. TPriorityMap is functionally backward compatible + * with {@link TMap}. + * + * To get the number of the items in the map, use {@link getCount}. + * TPriorityMap can also be used like a regular array as follows, + * + * $map[$key]=$value; // add a key-value pair + * unset($map[$key]); // remove the value with the specified key + * if(isset($map[$key])) // if the map contains the key + * foreach($map as $key=>$value) // traverse the items in the map + * $n=count($map); // returns the number of items in the map + * + * Using standard array access method like these will always use + * the default priority. + * + * An item that doesn't specify a priority will receive the default + * priority. The default priority is set during the instantiation + * of a new TPriorityMap. If no custom default priority is specified, + * the standard default priority of 10 is used. + * + * Priorities with significant digits below precision will be rounded. + * + * A priority may also be a numeric with decimals. This is set + * during the instantiation of a new TPriorityMap. + * The default is 8 decimal places for a priority. If a negative number + * is used, rounding occurs into the integer space rather than in + * the decimal space. See {@link round}. + * + * @author Brad Anderson + * @version $Id: TPriorityMap.php 2817 2010-04-18 04:25:03Z javalizard $ + * @package System.Collections + * @since 3.2a + */ +Prado::using('System.Collections.TMap'); + +class TPriorityMap extends TMap +{ + /** + * @var array internal data storage + */ + private $_d=array(); + /** + * @var boolean whether this list is read-only + */ + private $_r=false; + /** + * @var boolean indicates if the _d is currently ordered. + */ + private $_o=false; + /** + * @var array cached flattened internal data storage + */ + private $_fd=null; + /** + * @var integer number of items contain within the map + */ + private $_c=0; + /** + * @var numeric the default priority of items without specified priorities + */ + private $_dp=10; + /** + * @var integer the precision of the numeric priorities within this priority list. + */ + private $_p=8; + + /** + * Constructor. + * Initializes the array with an array or an iterable object. + * @param map|array|Iterator|TPriorityMap the intial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only + * @param numeric the default priority of items without specified priorities. + * @param integer the precision of the numeric priorities + * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + $this->setPrecision($precision); + $this->setDefaultPriority($defaultPriority); + } + + /** + * @return boolean whether this map is read-only or not. Defaults to false. + */ + public function getReadOnly() + { + return $this->_r; + } + + /** + * @param boolean whether this list is read-only or not + */ + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + + /** + * @return numeric gets the default priority of inserted items without a specified priority + */ + public function getDefaultPriority() + { + return $this->_dp; + } + + /** + * This must be called internally or when instantiated. + * @param numeric sets the default priority of inserted items without a specified priority + */ + protected function setDefaultPriority($value) + { + $this->_dp = (string)round(TPropertyValue::ensureFloat($value), $this->_p); + } + + /** + * @return integer The precision of numeric priorities, defaults to 8 + */ + public function getPrecision() + { + return $this->_p; + } + + /** + * This must be called internally or when instantiated. + * @param integer The precision of numeric priorities. + */ + protected function setPrecision($value) + { + $this->_p=TPropertyValue::ensureInteger($value); + } + + /** + * Returns an iterator for traversing the items in the map. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the map. + */ + public function getIterator() + { + return new ArrayIterator($this->flattenPriorities()); + } + + + /** + * Orders the priority list internally. + */ + protected function sortPriorities() { + if(!$this->_o) { + ksort($this->_d, SORT_NUMERIC); + $this->_o=true; + } + } + + /** + * This flattens the priority map into a flat array [0,...,n-1] + * @return array array of items in the list in priority and index order + */ + protected function flattenPriorities() { + if(is_array($this->_fd)) + return $this->_fd; + + $this->sortPriorities(); + $this->_fd = array(); + foreach($this->_d as $priority => $itemsatpriority) + $this->_fd = array_merge($this->_fd, $itemsatpriority); + return $this->_fd; + } + + /** + * Returns the number of items in the map. + * This method is required by Countable interface. + * @return integer number of items in the map. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer the number of items in the map + */ + public function getCount() + { + return $this->_c; + } + + /** + * Gets the number of items at a priority within the map. + * @param numeric optional priority at which to count items. if no parameter, + * it will be set to the default {@link getDefaultPriority} + * @return integer the number of items in the map at the specified priority + */ + public function getPriorityCount($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if(!isset($this->_d[$priority])||!is_array($this->_d[$priority])) + return false; + return count($this->_d[$priority]); + } + + /** + * This returns a list of the priorities within this map, ordered lowest to highest. + * @return array the array of priority numerics in decreasing priority order + */ + public function getPriorities() + { + $this->sortPriorities(); + return array_keys($this->_d); + } + + /** + * Returns the keys within the map ordered through the priority of each key-value pair + * @return array the key list + */ + public function getKeys() + { + return array_keys($this->flattenPriorities()); + } + + /** + * Returns the item with the specified key. If a priority is specified, only items + * within that specific priority will be selected + * @param mixed the key + * @param mixed the priority. null is the default priority, false is any priority, + * and numeric is a specific priority. default: false, any priority. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key,$priority=false) + { + if($priority===false){ + $map=$this->flattenPriorities(); + return isset($map[$key])?$map[$key]:null; + } else { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + return (isset($this->_d[$priority])&&isset($this->_d[$priority][$key]))?$this->_d[$priority][$key]:null; + } + } + + /** + * This changes an item's priority. Specify the item and the new priority. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the key + * @param numeric|null the priority. default: null, filled in with the default priority numeric. + * @return numeric old priority of the item + */ + public function setPriorityAt($key,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + $oldpriority=$this->priorityAt($key); + if($oldpriority!==false&&$oldpriority!=$priority) { + $value=$this->remove($key,$oldpriority); + $this->add($key,$value,$priority); + } + return $oldpriority; + } + + /** + * Gets all the items at a specific priority. + * @param numeric priority of the items to get. Defaults to null, filled in with the default priority, if left blank. + * @return array all items at priority in index order, null if there are no items at that priority + */ + public function itemsAtPriority($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + return isset($this->_d[$priority])?$this->_d[$priority]:null; + } + + /** + * Returns the priority of a particular item within the map. This searches the map for the item. + * @param mixed item to look for within the map + * @return numeric priority of the item in the map + */ + public function priorityOf($item) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(($index=array_search($item,$items,true))!==false) + return $priority; + return false; + } + + /** + * Retutrns the priority of an item at a particular flattened index. + * @param integer index of the item within the map + * @return numeric priority of the item in the map + */ + public function priorityAt($key) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(array_key_exists($key,$items)) + return $priority; + return false; + } + + /** + * Adds an item into the map. A third parameter may be used to set the priority + * of the item within the map. Priority is primarily used during when flattening + * the map into an array where order may be and important factor of the key-value + * pairs within the array. + * Note, if the specified key already exists, the old value will be overwritten. + * No duplicate keys are allowed regardless of priority. + * @param mixed key + * @param mixed value + * @param numeric|null priority, default: null, filled in with default priority + * @return numeric priority at which the pair was added + * @throws TInvalidOperationException if the map is read-only + */ + public function add($key,$value,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if(!$this->_r) + { + foreach($this->_d as $innerpriority=>$items) + if(array_key_exists($key,$items)) + { + unset($this->_d[$innerpriority][$key]); + $this->_c--; + if(count($this->_d[$innerpriority])===0) + unset($this->_d[$innerpriority]); + } + if(!isset($this->_d[$priority])) { + $this->_d[$priority]=array($key=>$value); + $this->_o=false; + } + else + $this->_d[$priority][$key]=$value; + $this->_c++; + $this->_fd=null; + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + return $priority; + } + + /** + * Removes an item from the map by its key. If no priority, or false, is specified + * then priority is irrelevant. If null is used as a parameter for priority, then + * the priority will be the default priority. If a priority is specified, or + * the default priority is specified, only key-value pairs in that priority + * will be affected. + * @param mixed the key of the item to be removed + * @param numeric|false|null priority. False is any priority, null is the + * default priority, and numeric is a specific priority + * @return mixed the removed value, null if no such key exists. + * @throws TInvalidOperationException if the map is read-only + */ + public function remove($key,$priority=false) + { + if(!$this->_r) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + + if($priority===false) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(array_key_exists($key,$items)) + { + $value=$this->_d[$priority][$key]; + unset($this->_d[$priority][$key]); + $this->_c--; + if(count($this->_d[$priority])===0) + { + unset($this->_d[$priority]); + $this->_o=false; + } + $this->_fd=null; + return $value; + } + return null; + } + else + { + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(isset($this->_d[$priority])&&(isset($this->_d[$priority][$key])||array_key_exists($key,$this->_d[$priority]))) + { + $value=$this->_d[$priority][$key]; + unset($this->_d[$priority][$key]); + $this->_c--; + if(count($this->_d[$priority])===0) { + unset($this->_d[$priority]); + $this->_o=false; + } + $this->_fd=null; + return $value; + } + else + return null; + } + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + + /** + * Removes all items in the map. {@link remove} is called on all items. + */ + public function clear() + { + foreach($this->_d as $priority=>$items) + foreach(array_keys($items) as $key) + $this->remove($key); + } + + /** + * @param mixed the key + * @return boolean whether the map contains an item with the specified key + */ + public function contains($key) + { + $map=$this->flattenPriorities(); + return isset($map[$key])||array_key_exists($key,$map); + } + + /** + * When the map is flattened into an array, the priorities are taken into + * account and elements of the map are ordered in the array according to + * their priority. + * @return array the list of items in array + */ + public function toArray() + { + return $this->flattenPriorities(); + } + + /** + * Combines the map elements which have a priority below the parameter value + * @param numeric the cut-off priority. All items of priority less than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: false, not inclusive. + * @return array the array of priorities keys with values of arrays of items that are below a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayBelowPriority($priority,$inclusive=false) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) + break; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + /** + * Combines the map elements which have a priority above the parameter value + * @param numeric the cut-off priority. All items of priority greater than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: true, inclusive. + * @return array the array of priorities keys with values of arrays of items that are above a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayAbovePriority($priority,$inclusive=true) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) + continue; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + /** + * Copies iterable data into the map. + * Note, existing data in the map will be cleared first. + * @param mixed the data to be copied from, must be an array, object implementing + * Traversable, or a TPriorityMap + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function copyFrom($data) + { + if($data instanceof TPriorityMap) + { + if($this->getCount()>0) + $this->clear(); + foreach($data->getPriorities() as $priority) { + foreach($data->itemsAtPriority($priority) as $key => $value) { + $this->add($key,$value,$priority); + } + } + } + else if(is_array($data)||$data instanceof Traversable) + { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Merges iterable data into the map. + * Existing data in the map will be kept and overwritten if the keys are the same. + * @param mixed the data to be merged with, must be an array, object implementing + * Traversable, or a TPriorityMap + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function mergeWith($data) + { + if($data instanceof TPriorityMap) + { + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $key => $value) + $this->add($key,$value,$priority); + } + } + else if(is_array($data)||$data instanceof Traversable) + { + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return $this->contains($offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } +} \ No newline at end of file diff --git a/tests/unit/Collections/TPriorityListTest.php b/tests/unit/Collections/TPriorityListTest.php index f329538c..35c712ae 100644 --- a/tests/unit/Collections/TPriorityListTest.php +++ b/tests/unit/Collections/TPriorityListTest.php @@ -964,6 +964,77 @@ class TPriorityListTest extends PHPUnit_Framework_TestCase $this->assertEquals($this->pitem3, $array[100][0]); } + public function testToArrayBelowPriority() { + $array = $this->plist->toArrayBelowPriority(0); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals(1, count($array)); + + $array = $this->plist->toArrayBelowPriority(10); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals(1, count($array)); + + $array = $this->plist->toArrayBelowPriority(10, true); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals($this->pitem1, $array[1]); + $this->assertEquals($this->pitem2, $array[2]); + $this->assertEquals(3, count($array)); + + $array = $this->plist->toArrayBelowPriority(11); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals($this->pitem1, $array[1]); + $this->assertEquals($this->pitem2, $array[2]); + $this->assertEquals(3, count($array)); + + $array = $this->plist->toArrayBelowPriority(100); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals($this->pitem1, $array[1]); + $this->assertEquals($this->pitem2, $array[2]); + $this->assertEquals(3, count($array)); + + $array = $this->plist->toArrayBelowPriority(100, true); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals($this->pitem1, $array[1]); + $this->assertEquals($this->pitem2, $array[2]); + $this->assertEquals($this->pitem3, $array[3]); + $this->assertEquals(4, count($array)); + } + + public function testToArrayAbovePriority() { + $array = $this->plist->toArrayAbovePriority(100, false); + $this->assertEquals(0, count($array)); + + $array = $this->plist->toArrayAbovePriority(100, true); + $this->assertEquals(1, count($array)); + $this->assertEquals($this->pitem3, $array[0]); + + $array = $this->plist->toArrayAbovePriority(10, false); + $this->assertEquals($this->pitem3, $array[0]); + $this->assertEquals(1, count($array)); + + $array = $this->plist->toArrayAbovePriority(10); + $this->assertEquals($this->pitem1, $array[0]); + $this->assertEquals($this->pitem2, $array[1]); + $this->assertEquals($this->pitem3, $array[2]); + $this->assertEquals(3, count($array)); + + $array = $this->plist->toArrayAbovePriority(11); + $this->assertEquals($this->pitem3, $array[0]); + $this->assertEquals(1, count($array)); + + $array = $this->plist->toArrayAbovePriority(0); + $this->assertEquals($this->pitem1, $array[0]); + $this->assertEquals($this->pitem2, $array[1]); + $this->assertEquals($this->pitem3, $array[2]); + $this->assertEquals(3, count($array)); + + $array = $this->plist->toArrayAbovePriority(-10000000, true); + $this->assertEquals($this->pfirst, $array[0]); + $this->assertEquals($this->pitem1, $array[1]); + $this->assertEquals($this->pitem2, $array[2]); + $this->assertEquals($this->pitem3, $array[3]); + $this->assertEquals(4, count($array)); + } + public function testArrayReadTPriorityList() { $this->assertTrue($this->plist[0] === $this->pfirst); diff --git a/tests/unit/Collections/TPriorityMapTest.php b/tests/unit/Collections/TPriorityMapTest.php new file mode 100644 index 00000000..54ff68b7 --- /dev/null +++ b/tests/unit/Collections/TPriorityMapTest.php @@ -0,0 +1,490 @@ +map=new TPriorityMap; + $this->item1=new MapItem; + $this->item2=new MapItem; + $this->item3=new MapItem; + $this->item4=new MapItem; + $this->item5=new MapItem; + $this->map->add('key1',$this->item1); + $this->map->add('key2',$this->item2); + + //Test the priority capabilities + } + + public function setUpPriorities() { + $this->map->add('key3', $this->item3, 0); + $this->map->add('key4', $this->item4, 100); + $this->map->add('key5', $this->item5, 1); + } + + public function tearDown() { + $this->map=null; + $this->item1=null; + $this->item2=null; + $this->item3=null; + } + + public function testConstruct() { + $a=array(1,2,'key3'=>3); + $map=new TPriorityMap($a); + $this->assertEquals(3,$map->getCount()); + $map2=new TPriorityMap($this->map); + $this->assertEquals(2,$map2->getCount()); + + /* Test the priority functionality of TPriorityMap */ + + $map3=new TPriorityMap($this->map, false, 100, -1); + $this->assertEquals(100,$map3->getDefaultPriority()); + $this->assertEquals(-1,$map3->getPrecision()); + } + + /* Test that TPriorityMap complies with TMap */ + + + public function testGetReadOnly() { + $map = new TPriorityMap(null, true); + self::assertEquals(true, $map->getReadOnly(), 'List is not read-only'); + $map = new TList(null, false); + self::assertEquals(false, $map->getReadOnly(), 'List is read-only'); + } + + public function testGetCount() { + $this->assertEquals(2,$this->map->getCount()); + } + + public function testGetKeys() { + $keys=$this->map->getKeys(); + $this->assertTrue(count($keys)===2 && $keys[0]==='key1' && $keys[1]==='key2'); + } + + public function testAdd() + { + $this->map->add('key3',$this->item3); + $this->assertTrue($this->map->getCount()==3 && $this->map->contains('key3')); + } + + public function testCanNotAddWhenReadOnly() { + $map = new TPriorityMap(array(), true); + try { + $map->add('key', 'value'); + } catch(TInvalidOperationException $e) { + return; + } + self::fail('An expected TInvalidOperationException was not raised'); + } + + public function testRemove() + { + $this->map->remove('key1'); + $this->assertTrue($this->map->getCount()==1 && !$this->map->contains('key1')); + $this->assertTrue($this->map->remove('unknown key')===null); + } + + public function testCanNotRemoveWhenReadOnly() { + $map = new TPriorityMap(array('key' => 'value'), true); + try { + $map->remove('key'); + } catch(TInvalidOperationException $e) { + return; + } + self::fail('An expected TInvalidOperationException was not raised'); + } + + public function testClear() + { + $this->map->clear(); + $this->assertTrue($this->map->getCount()==0 && !$this->map->contains('key1') && !$this->map->contains('key2')); + } + + public function testContains() + { + $this->assertTrue($this->map->contains('key1')); + $this->assertTrue($this->map->contains('key2')); + $this->assertFalse($this->map->contains('key3')); + } + + public function testCopyFrom() + { + $array=array('key3'=>$this->item3,'key4'=>$this->item1); + $this->map->copyFrom($array); + $this->assertTrue($this->map->getCount()==2 && $this->map['key3']===$this->item3 && $this->map['key4']===$this->item1); + try + { + $this->map->copyFrom($this); + $this->fail('no exception raised when copying a non-traversable object'); + } + catch(TInvalidDataTypeException $e) + { + + } + } + + public function testMergeWith() + { + $array=array('key2'=>$this->item1,'key3'=>$this->item3); + $this->map->mergeWith($array); + $this->assertEquals(3,$this->map->getCount()); + $this->assertTrue($this->map['key2']===$this->item1); + $this->assertTrue($this->map['key3']===$this->item3); + try + { + $this->map->mergeWith($this); + $this->fail('no exception raised when copying a non-traversable object'); + } + catch(TInvalidDataTypeException $e) + { + + } + } + + public function testArrayRead() + { + $this->assertTrue($this->map['key1']===$this->item1); + $this->assertTrue($this->map['key2']===$this->item2); + $this->assertEquals(null,$this->map['key3']); + } + + public function testArrayWrite() + { + $this->map['key3']=$this->item3; + $this->assertTrue($this->map['key3']===$this->item3); + $this->assertEquals(3,$this->map->getCount()); + $this->map['key1']=$this->item3; + $this->assertTrue($this->map['key1']===$this->item3); + $this->assertEquals(3,$this->map->getCount()); + unset($this->map['key2']); + $this->assertEquals(2,$this->map->getCount()); + $this->assertFalse($this->map->contains('key2')); + try + { + unset($this->map['unknown key']); + + } + catch(Exception $e) + { + $this->fail('exception raised when unsetting element with unknown key'); + } + } + + public function testArrayForeach() + { + $n=0; + $found=0; + foreach($this->map as $index=>$item) + { + $n++; + if($index==='key1' && $item===$this->item1) + $found++; + if($index==='key2' && $item===$this->item2) + $found++; + } + $this->assertTrue($n==2 && $found==2); + } + + public function testArrayMisc() + { + $this->assertEquals($this->map->Count,count($this->map)); + $this->assertTrue(isset($this->map['key1'])); + $this->assertFalse(isset($this->map['unknown key'])); + } + + public function testToArray() { + $map = new TPriorityMap(array('key' => 'value')); + self::assertEquals(array('key' => 'value'), $map->toArray()); + } + + + + + /* Test the priority functionality of TPriorityMap */ + + + public function testDefaultPriorityAndPrecision() { + + $this->assertEquals(10, $this->map->DefaultPriority); + + $this->map->DefaultPriority = 5; + $this->assertEquals(5, $this->map->getDefaultPriority()); + + $this->assertEquals(8, $this->map->Precision); + + $this->map->Precision = 0; + $this->assertEquals(0, $this->map->getPrecision()); + + ; + + $this->assertEquals(5, $this->map->add('key3', $this->item3)); + $this->assertEquals(10, $this->map->add('key4', $this->item1, 10)); + $this->assertTrue(10 == $this->map->add('key4', $this->item1, 10.01)); + $this->assertTrue(100 == $this->map->add('key4', $this->item1, 100)); + $this->map->Precision = 1; + $this->assertTrue(10.1 == $this->map->add('key5', $this->item1, 10.1)); + + $this->assertEquals(5, $this->map->getCount()); + } + + public function testAddWithPriorityAndPriorityOfAt() { + + $this->setUpPriorities(); + + $this->assertEquals(5, $this->map->getCount()); + $this->assertEquals(10, $this->map->priorityOf($this->item1)); + $this->assertEquals(0, $this->map->priorityOf($this->item3)); + $this->assertEquals(100, $this->map->priorityOf($this->item4)); + $this->assertEquals(1, $this->map->priorityOf($this->item5)); + $this->assertEquals(false, $this->map->priorityOf(null)); + $this->assertEquals(false, $this->map->priorityOf('foo')); + + $this->assertEquals(10, $this->map->priorityAt('key1')); + $this->assertEquals(0, $this->map->priorityAt('key3')); + $this->assertEquals(100, $this->map->priorityAt('key4')); + $this->assertEquals(1, $this->map->priorityAt('key5')); + $this->assertEquals(false, $this->map->priorityAt(null)); + $this->assertEquals(false, $this->map->priorityAt('foo')); + + } + + public function testRemoveWithPriorityAndItemsAtWithPriority() { + + $this->setUpPriorities(); + + $this->assertEquals(5, $this->map->getCount()); + $this->map->remove('key6'); + $this->assertEquals(5, $this->map->getCount()); + $this->map->remove('key6', null); + $this->assertEquals(5, $this->map->getCount()); + + + // key5 is at priority 1... not the default priority defined by null... nothing should happen here + $this->map->remove('key5', null); + $this->assertEquals(5, $this->map->getCount()); + + // key5 is at priority 1... not 50... nothing should happen here + $this->map->remove('key5', 50); + $this->assertEquals(5, $this->map->getCount()); + + + + $this->assertEquals(array('key3'=>$this->item3), $this->map->itemsAtPriority(0)); + $this->assertEquals(array('key1'=>$this->item1, 'key2'=>$this->item2), $this->map->itemsAtPriority($this->map->DefaultPriority)); + + $this->assertEquals($this->item2, $this->map->itemAt('key2')); + $this->assertEquals($this->item2, $this->map->itemAt('key2', 10)); + $this->assertNull($this->map->itemAt('key2', 11)); //'key2' doesn't exist and priority 11... it is only at priority 10 + $this->assertNull($this->map->itemAt('key2', 10.1)); //'key2' doesn't exist and priority 10.1... it is only at priority 10 + + $this->assertEquals($this->item4, $this->map->remove('key4')); + $this->assertEquals(4, $this->map->getCount()); + + $this->assertEquals($this->item5, $this->map->remove('key5')); + $this->assertEquals(3, $this->map->getCount()); + } + public function testIteratorAndArrayWithPriorities() { + + $this->setUpPriorities(); + + // This is the primary reason for a TPriorityMap + $array = $this->map->toArray(); + + $ordered_keys = array_keys($array); + $this->assertEquals('key3', $ordered_keys[0]); + $this->assertEquals('key5', $ordered_keys[1]); + $this->assertEquals('key1', $ordered_keys[2]); + $this->assertEquals('key2', $ordered_keys[3]); + $this->assertEquals('key4', $ordered_keys[4]); + + $ordered_values = array_values($array); + $this->assertEquals($this->item3, $ordered_values[0]); + $this->assertEquals($this->item5, $ordered_values[1]); + $this->assertEquals($this->item1, $ordered_values[2]); + $this->assertEquals($this->item2, $ordered_values[3]); + $this->assertEquals($this->item4, $ordered_values[4]); + + $iter = $this->map->getIterator(); + + $this->assertTrue($iter->valid()); + $this->assertEquals('key3', $iter->key()); + $this->assertEquals($this->item1, $iter->current()); + $iter->next(); + $this->assertTrue($iter->valid()); + $this->assertEquals('key5', $iter->key()); + $this->assertEquals($this->item3, $iter->current()); + $iter->next(); + $this->assertTrue($iter->valid()); + $this->assertEquals('key1', $iter->key()); + $this->assertEquals($this->item5, $iter->current()); + $iter->next(); + $this->assertTrue($iter->valid()); + $this->assertEquals('key2', $iter->key()); + $this->assertEquals($this->item5, $iter->current()); + $iter->next(); + $this->assertTrue($iter->valid()); + $this->assertEquals('key4', $iter->key()); + $this->assertEquals($this->item5, $iter->current()); + $iter->next(); + $this->assertFalse($iter->valid()); + $this->assertEquals(null, $iter->key()); + $this->assertEquals(null, $iter->current()); + } + + + public function testGetPriorities() { + $this->setUpPriorities(); + + $priorities = $this->map->getPriorities(); + + $this->assertEquals(0, $priorities[0]); + $this->assertEquals(1, $priorities[1]); + $this->assertEquals(10, $priorities[2]); + $this->assertEquals(100, $priorities[3]); + $this->assertEquals(null, $priorities[4]); + } + + + public function testCopyAndMergeWithPriorities() { + $this->setUpPriorities(); + + $map1 = new TPriorityMap(); + $map1->add('key1', $this->item1); + $map1->add('keyc', 'valuec'); + $map1->copyFrom($this->map); + + $this->assertEquals(5, $map1->getCount()); + + $array = $map1->toArray(); + $ordered_keys = array_keys($array); + $this->assertEquals('key3', $ordered_keys[0]); + $this->assertEquals('key5', $ordered_keys[1]); + $this->assertEquals('key1', $ordered_keys[2]); + $this->assertEquals('key2', $ordered_keys[3]); + $this->assertEquals('key4', $ordered_keys[4]); + + $ordered_values = array_values($array); + $this->assertEquals($this->item3, $ordered_values[0]); + $this->assertEquals($this->item5, $ordered_values[1]); + $this->assertEquals($this->item1, $ordered_values[2]); + $this->assertEquals($this->item2, $ordered_values[3]); + $this->assertEquals($this->item4, $ordered_values[4]); + + + + $map2 = new TPriorityMap(); + $map2->add('startkey', 'startvalue', -1000); + $map2->add('key5', 'value5', 40); + $map2->add('endkey', 'endvalue', 1000); + $map2->mergeWith($this->map); + + $this->assertEquals(7, $map2->getCount()); + + $array = $map2->toArray(); + $ordered_keys = array_keys($array); + $this->assertEquals('startkey', $ordered_keys[0]); + $this->assertEquals('key3', $ordered_keys[1]); + $this->assertEquals('key5', $ordered_keys[2]); + $this->assertEquals('key1', $ordered_keys[3]); + $this->assertEquals('key2', $ordered_keys[4]); + $this->assertEquals('key4', $ordered_keys[5]); + $this->assertEquals('endkey', $ordered_keys[6]); + + $ordered_values = array_values($array); + $this->assertEquals('startvalue', $ordered_values[0]); + $this->assertEquals($this->item3, $ordered_values[1]); + $this->assertEquals($this->item5, $ordered_values[2]); + $this->assertEquals($this->item1, $ordered_values[3]); + $this->assertEquals($this->item2, $ordered_values[4]); + $this->assertEquals($this->item4, $ordered_values[5]); + $this->assertEquals('endvalue', $ordered_values[6]); + + $this->assertEquals(1, $map2->priorityAt('key5')); + $this->assertEquals(1, $map2->priorityOf($this->item5)); + } + + public function testSetPriorityAt() { + + $this->assertEquals(10, $this->map->priorityAt('key2')); + $this->assertEquals(10, $this->map->setPriorityAt('key2', 1)); + $this->assertEquals(1, $this->map->priorityAt('key2')); + $this->assertEquals(1, $this->map->setPriorityAt('key2')); + $this->assertEquals(10, $this->map->priorityAt('key2')); + } + + public function testToArrayBelowPriority() { + $this->setUpPriorities(); + + $array = $this->map->toArrayBelowPriority(1); + $this->assertEquals(array('key3'=> $this->item3), $array); + $this->assertEquals(1, count($array)); + + $array = $this->map->toArrayBelowPriority(1, true); + $this->assertEquals(array('key3'=> $this->item3, 'key5'=> $this->item5), $array); + $this->assertEquals(2, count($array)); + + $array = $this->map->toArrayBelowPriority(2); + $this->assertEquals(array('key3'=> $this->item3, 'key5'=> $this->item5), $array); + $this->assertEquals(2, count($array)); + + $array = $this->map->toArrayBelowPriority(10); + $this->assertEquals(array('key3'=> $this->item3, 'key5'=> $this->item5), $array); + $this->assertEquals(2, count($array)); + + $array = $this->map->toArrayBelowPriority(10, true); + $this->assertEquals(array('key3'=> $this->item3, 'key5'=> $this->item5, 'key1' => $this->item1, 'key2' => $this->item2), $array); + $this->assertEquals(4, count($array)); + + $array = $this->map->toArrayBelowPriority(100); + $this->assertEquals(array('key3'=> $this->item3, 'key5'=> $this->item5, 'key1' => $this->item1, 'key2' => $this->item2), $array); + $this->assertEquals(4, count($array)); + + $array = $this->map->toArrayBelowPriority(100, true); + $this->assertEquals(array('key3'=> $this->item3, 'key5'=> $this->item5, 'key1' => $this->item1, 'key2' => $this->item2, 'key4' => $this->item4), $array); + $this->assertEquals(5, count($array)); + } + + public function testToArrayAbovePriority() { + $this->setUpPriorities(); + + $array = $this->map->toArrayAbovePriority(100, false); + $this->assertEquals(0, count($array)); + + $array = $this->map->toArrayAbovePriority(100, true); + $this->assertEquals(1, count($array)); + $this->assertEquals(array('key4' => $this->item4), $array); + + $array = $this->map->toArrayAbovePriority(11); + $this->assertEquals(array('key4' => $this->item4), $array); + $this->assertEquals(1, count($array)); + + $array = $this->map->toArrayAbovePriority(10, false); + $this->assertEquals(array('key4' => $this->item4), $array); + $this->assertEquals(1, count($array)); + + $array = $this->map->toArrayAbovePriority(10); + $this->assertEquals(array('key1' => $this->item1, 'key2' => $this->item2, 'key4' => $this->item4), $array); + $this->assertEquals(3, count($array)); + + $array = $this->map->toArrayAbovePriority(0); + $this->assertEquals(array('key3' => $this->item3, 'key5' => $this->item5, 'key1' => $this->item1, 'key2' => $this->item2, 'key4' => $this->item4), $array); + $this->assertEquals(5, count($array)); + } + + + +} + +?> -- cgit v1.2.3