select('is_readable')->map('ucfirst')->natsort()->join("
"); # @endcode It's worth noting that every method call creates a new object: @code $a = new _array('c', 'a', 'b'); $b = $a->sort(); echo $a; # "c, a, b" echo $b; # "a, b, c" @endcode Exception are few modificator methods like _array::pop(). Along with main class the package contains a set of utility functions that provide syntactic sugar for creating _array objects from different sources: @code $ary = _array('one', 'two'); $server = _a($_SERVER); $words = _w('foo bar baz'); @endcode @ref _array "_array Class Reference" @ref _array.inc.php "utility functions" */ /** @file */ /** * Populates an _array using paramters as values. * * @code * $ary = _array(1, 2, 3); * echo $ary; # "1, 2, 3" * @endcode */ function _array() { $_ = func_get_args(); $r = new _array(); $r->set($_); return $r; } /** * Converts a php array to an _array object. * * @code * $foo = array('foo', 'bar'); * $ary = _a($foo); * echo $ary; # "foo, bar" * @endcode */ function _a($a = array()) { $r = new _array(); return $r->set($a); } /** * Converts the string to _array object by splitting it on the delimiter. * The delimiter defaults to " " (a single space). * * @code * $words = _w('foo bar baz'); * echo $words; # "foo, bar, baz" * @endcode */ function _w($str, $delim = ' ') { $r = new _array(); return $r->set(explode($delim, $str)); } /// Array wrapper. class _array implements ArrayAccess, Countable, IteratorAggregate { // -------------------------------------------------------------------------------- // SPL STUFF. function __construct() { $this->a = func_get_args(); } function __toString() { return implode(', ', $this->_flatten($this->a)); } function __clone() { return $this->_new($this->a); } function offsetExists($k) { return isset($this->a[$k]); } function offsetGet($k) { return $this->a[$k]; } function offsetSet($k, $v) { strlen($k) ? ($this->a[$k] = $v) : ($this->a[] = $v); } function offsetUnset($k) { unset($this->a[$k]); } function getIterator() { return new ArrayIterator($this->a); } /**@name Basic operations. * @{ */ /** * Returns the number of elements. * If $recurse is true, also counts elements in subarrays. * * @code * echo _array(1, 2, 3)->count(); # 3 * * $a = _array(1, _array(2, _array(3, 4)), 5); * echo $a->count(); # 3 * echo $a->count(true); # 5 * @endcode */ function count($recurse = false) { return $recurse ? $this->_count($this->a) : count($this->a); } /** * Creates a copy of the current object. * * @code * $a = _array(1, 2, 3); * $b = $a->copy(); * $b[0] = 9; * echo $a; # "1, 2, 3" * echo $b; # "9, 2, 3" * @endcode */ function copy() { return clone $this; } /** * Converts this object to a php array. * If $recurse, nested _array objects are also converted. * * @code * $ary = _array(1, 2, 3); * echo implode(",", $ary->a()); # "1,2,3" * @endcode */ function a($recurse = false) { if(!$recurse) return $this->a; $b = array(); foreach($this->a as $k => $v) $b[$k] = ($v instanceof self) ? $v->a(true) : $v; return $b; } /** * Alias for a(). */ function to_array($recurse = false) { return $this->a($recurse); } /** * Creates new _array object from the values of this array. * * @code * $a = _a(array('x' => 1, 'y' => 10)); * echo $a->values(); # "1, 10" * @endcode */ function values() { return $this->_new(array_values($this->a)); } /** * Creates new _array object from the keys of this array. * * @code * $a = _a(array('x' => 1, 'y' => 10)); * echo $a->keys(); # "x, y" * @endcode */ function keys() { return $this->_new(array_keys($this->a)); } /* @}*/ /**@name Populating an array. * @{ */ /** * Sets the underlying "primitive" array value of the current object. */ function set($array) { $this->a = is_array($array) ? $array : array($array); return $this; } /** * Populates this array by splitting a string in chunks. * * @code * $chars = _array()->from_string('abcd'); * * echo $chars; # "a, b, c, d" * @endcode */ function from_string($str, $len = 1) { $this->a = str_split($str, $len); return $this; } /** * Populates this array by splitting a text by lines. * Trims whitespace from each line and removes empty lines. * * @code * $lines = _a()->from_text(' * lorem * ipsum * dolor * * '); * echo $lines; # "lorem, ipsum, dolor" * @endcode */ function from_text($str) { $this->a = array_filter(array_map('trim', explode("\n", $str))); return $this; } /** * Populates this array by repeatedly calling a callback. * The callback is called until it returns false (or equivalent). * Extra parameters, if supplied, are passed to the callback. * * @code * $query = mysql_query("SELECT * FROM users"); ## * $users = _array()->from_call('mysql_fetch_assoc', $query); ## * @endcode */ function from_call($func) { $_ = func_get_args(); array_shift($_); $b = array(); while($v = call_user_func_array($func, $_)) $b[] = $v; $this->a = $b; return $this; } /* @}*/ /**@name Element retrieval. * @{ */ /** * Returns an element with the key $key. * If the key is not found, returns null (no warning issued). * If called with no arguments, returns the first element. * * @code * $ary = _a(array('foo' => 1, 'bar' => 5)); * echo $ary->e('bar'); # "5" * $ary->e('BAZ'); # null * @endcode */ function e($key = null) { return is_null($key) ? reset($this->a) : (isset($this->a[$key]) ? $this->a[$key] : null); } /** * Alias for e(). */ function item($key = null) { return is_null($key) ? reset($this->a) : (isset($this->a[$key]) ? $this->a[$key] : null); } /** * Returns n-th element of the array. * Negative offsets are counted from the end (-1 = last element). * * @code * $ary = _w('A B C D'); * echo $ary->n(0); # "A" * echo $ary->n(1); # "B" * echo $ary->n(-1); # "D" * echo $ary->n(-2); # "C" * $ary->n(9); # null * @endcode */ function n($offset = 0) { $b = array_slice($this->a, $offset, 1); return count($b) ? reset($b) : null; } /** * Returns the maximal element. * * @code * echo _array(10, 4, 20, 1)->max(); # 20 * @endcode */ function max() { return max($this->a); } /** * Returns the minimal element. * * @code * echo _array(10, 4, 20, 1)->min(); # 1 * @endcode */ function min() { return min($this->a); } /** * Returns a random element. * * @code * $a = _w('A B C D'); * echo $a->contains($a->rand()); # true * @endcode */ function rand() { return $this->a[array_rand($this->a)]; } /** * Searches for the value and returns its key (or null). * * @code * $a = _w('a b c d'); * echo $a->find('c'); # 2 * @endcode */ function find($value, $strict = false) { $b = array_search($value, $this->a, $strict); return $b === false ? null : $b; } /** * Searches for the value and returns its offset (or -1). * * @code * $a = _a(array('a' => 1, 'b' => 2, 'c' => 3)); * echo $a->find(2); # 'b' * echo $a->index(2); # 1 * echo $a->index(9); # -1 * @endcode */ function index($value, $strict = false) { $b = array_search($value, $this->a); return $b === false ? -1 : array_search($b, array_keys($this->a), true); } /** * Searches for the value and returns true or false. * * @code * $a = _w('a b c d'); * echo $a->contains('c'); # true * echo $a->contains('x'); # false * @endcode */ function contains($value, $strict = false) { return in_array($value, $this->a, $strict); } /* @}*/ /**@name Calling functions. * @{ */ /** * Applies a callback function to array elements. * The callback takes an array element as the first argument. * If called with more than one parameter, additional parameters are passed to the callback. * * @code * function mul($a, $b) { return $a * $b; } * * echo _array(4, 3, 2, 1)->map('mul', 5); # "20, 15, 10, 5" * @endcode */ function map($func) { $_ = func_get_args(); $b = array(); if(count($_) == 1) $b = array_map($func, $this->a); else foreach($this->a as $k => $v) { $_[0] = $v; $b[$k] = call_user_func_array($func, $_); } return $this->_new($b); } /** * Calls a method on each element. * All elements are considered to be objects that have a method $func. * If called with more than one parameter, additional parameters are passed to the method. * * @code * $a = _array(_w('a b c'), _w('A B C'), _w('0 1 2')); * echo $a->each('join', '/'); # "a/b/c, A/B/C, 0/1/2" * @endcode */ function each($func) { $_ = func_get_args(); array_shift($_); $b = array(); foreach($this->a as $k => $v) { $b[$k] = call_user_func_array(array($v, $func), $_); } return $this->_new($b); } /** * Recursively applies a callback function to array elements. * If an element is an array or _array, the callback is applied recursively to that array. * If called with more than one parameter, additional parameters are passed to the callback. * * @code * function no_vowels($s) { return preg_replace('/[aeiou]/', '', (string) $s); } * * $a = _array('lorem', 'ipsum', array('dolor', 'sit'), 'amet'); * * // map converts 2nd element to string (Array) * echo $a->map('no_vowels'); # "lrm, psm, Arry, mt" * * // rmap handles 2nd element as array * echo $a->rmap('no_vowels'); # "lrm, psm, dlr, st, mt" * @endcode */ function rmap($func) { $_ = func_get_args(); $b = $this->_rmap($this->a, $_); return $this->_new($b); } /** * Applies a callback function to array keys and elements. * The callback takes an array element as its first param and its key as the second. * If called with more than one parameter, additional parameters are passed to the callback. * * @code * function format($value, $key, $delim) { return "$key$delim$value"; } * * echo _w('A B C D')->kmap('format', '='); # "0=A, 1=B, 2=C, 3=D" * @endcode */ function kmap($func) { $_ = func_get_args(); array_unshift($_, ''); $b = array(); foreach($this->a as $k => $v) { $_[0] = $v; $_[1] = $k; $b[$k] = call_user_func_array($func, $_); } return $this->_new($b); } /** * Removes elements for which the callback function returns false. * If called with more than one parameter, additional parameters are passed to the callback. * * @code * $csv = _w('foo,bar,,,,baz', ',')->filter('strlen'); * echo $csv; # "foo, bar, baz" * * function less_than($a, $n) { return $a < $n; } * * $numbers = _array(7, 2, 11, 3, 77, 4); * echo $numbers->select('less_than', 10); # "7, 2, 3, 4" * @endcode */ function filter($func = null) { $_ = func_get_args(); return $this->_new($this->_filter($this->a, $_)); } /** * Alias for filter(). */ function select($func = null) { $_ = func_get_args(); return $this->_new($this->_filter($this->a, $_)); } /** * Removes elements for which the callback function returns true. * If called with more than one parameter, additional parameters are passed to the callback. * * @code * $numbers = _array(7, 2, 11, 3, 77, 4); * echo $numbers->reject('less_than', 5); # "7, 11, 77" * @endcode */ function reject($func = null) { $_ = func_get_args(); $b = $this->_filter($this->a, $_); return $this->_new(array_diff($this->a, $b)); } /** * Calls a method with the list of arguments. * * @code * $vowels = _w('a e i o u'); * echo _w('a b c d e f g i j k')->apply('remove', $vowels); # "b, c, d, f, g, j, k" * @endcode */ function apply($func, $args) { $_ = func_get_args(); array_splice($_, 0, 2); return call_user_func_array( array($this, $func), array_merge($this->_ary($args), $_) ); } /** * Calls a method on the first element. * Let $this be array of arrays. tab() takes the first element, converts it to _array, * and then calls the given method on it, passing the other members of $this as arguments. * * @code * $a = _array(_w('1 2 3 4'), _w('1 x 3 4'), _w('1 x 3 x')); * echo $a->tab('intersect'); # "1, 3" * @endcode */ function tab($func) { $b = $this->a; $c = array_shift($b); if(!$c instanceof self) $c = $this->_new($c); return call_user_func_array(array($c, $func), $b); } /* @}*/ /**@name Elements values. * @{ */ /** * Removes specific values from the array. * Takes a variable number of parameters and removes all of them from the array. * * @code * $a = _w('1 2 3 4 5 6 7 8'); * echo $a->remove(2, 4, 6, 8, 10); # "1, 3, 5, 7" * @endcode */ function remove() { $_ = func_get_args(); return $this->_new(array_diff($this->a, $_)); } /** * Collects elements matching regular expression. * If $matching is set to false, conversely, collects non-matching elements. * * @code * $a = _w('12 foo 34 bar')->grep('/[0-9]+/'); * echo $a; # "12, 34" * * $a = _w('12 foo 34 bar')->grep('/[0-9]+/', false); * echo $a; # "foo, bar" * @endcode */ function grep($regexp, $matching = true) { $b = preg_grep($regexp, $this->a, $matching ? 0 : PREG_GREP_INVERT); return $this->_new($b); } /** * Reduces the array to a single value using a callback. * Callback receives as argument $memo (accumulator value), * the current element and the rest of the parameters passed to inject. * * @code * function conc3($memo, $val, $delim) { return "$memo $delim $val"; } * * echo _w('oranges banana')->inject('conc3', 'apples', 'and'); # "apples and oranges and banana" * * function cmp($memo, $word) { return strlen($memo) > strlen($word) ? $memo : $word; } * * echo _w('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')->inject('cmp'); # "consectetuer" * @endcode */ function inject($func, $memo = null) { $_ = func_get_args(); foreach($this->a as $v) { $_[0] = $memo; $_[1] = $v; $memo = call_user_func_array($func, $_); } return $memo; } /** * Alias for inject(). */ function reduce($func, $memo = null) { $_ = func_get_args(); foreach($this->a as $v) { $_[0] = $memo; $_[1] = $v; $memo = call_user_func_array($func, $_); } return $memo; } /* @}*/ /// Sort flag - compare "normally" (no type conversinon). const NORMAL = 1; /// Sort flag - compare as numbers. const NUMERIC = 2; /// Sort flag - compare as strings, case sensitive. const ALPHA = 3; /// Sort flag - compare as strings, case insensitive. const NOCASE = 4; /// Sort flag - compare using natsort (http://php.net/natsort). const NATURAL = 5; /// Sort flag - compare using natsort (http://php.net/natcasesort). const NATCASE = 6; /**@name Sorting. * @{ */ /** * Sorts the array. * * If no parameter is given, sort is performed "normally" (as in http://php.net/sort). * If parameter is a callable, it is used as a sort callback (as in http://php.net/usort). * If parameter is numeric, it is treated as a flag. * If flag is negative (i.e. sort(- _array::ALPHA) or just sort(-1)), sorting is reversed. * * @code * echo _array(7, 5, 9, 1)->sort(); # "1, 5, 7, 9" * echo _array(7, 5, 9, 1)->sort(-1); # "9, 7, 5, 1" * * echo _w('a1 a2 a21 a11 a3')->sort(_array::ALPHA); # "a1, a11, a2, a21, a3" * echo _w('a1 a2 a21 a11 a3')->sort(_array::NATURAL); # "a1, a2, a3, a11, a21" * * function lencmp($a, $b) { return strlen($a) - strlen($b); } * * $a = _w('Lorem ipsum dolor sit amet consectetuer adipiscing elit'); * echo $a->sort('lencmp'); # "sit, elit, amet, Lorem, ipsum, dolor, adipiscing, consectetuer" * @endcode */ function sort($param = null) { $b = $this->a; if(func_num_args() == 0) { sort($b); } else if(is_callable($param)) { usort($b, $param); } else { $param = (int) $param; switch(abs($param)) { case self::NUMERIC: sort($b, SORT_NUMERIC); break; case self::ALPHA: sort($b, SORT_STRING); break; case self::NOCASE: usort($b, 'strcasecmp'); break; case self::NATURAL: natsort($b); break; case self::NATCASE: natcasesort($b); break; default: sort($b); break; } if($param < 0) $b = array_reverse($b); } return $this->_new(array_values($b)); } /** * Alias for sort(_array::NATURAL). */ function natsort($param = null) { $c = self::NATURAL; if(intval($param) < 0) $c = -$c; return $this->sort($c); } /** * Sorts array by key. * $param is the same as in sort() */ function ksort($param = null) { $b = array(); foreach($this->keys()->sort($param) as $key) $b[$key] = $this->a[$key]; return $this->_new($b); } /* @}*/ /**@name Slicing and subselecting. * @{ */ /** * Returns a subarray as specified by $offset and $len parameters. * $offset and $len as in http://www.php.net/array_slice. * * @code * $a = _w('a b c d e f'); * echo $a->slice(2, 3); # "c, d, e" * echo $a->slice(-3, 3); # "d, e, f" * @endcode */ function slice($offset, $len = null) { switch(func_num_args()) { case 1: $b = array_slice($this->a, $offset); break; case 2: $b = array_slice($this->a, $offset, $len); break; } return $this->_new(array_values($b)); } /** * Returns a subarray from the elements between two indexes (inclusive). * Negative indexes are counted from the end. * * @code * $a = _w('a b c d e f'); * echo $a->sub(2, 4); # "c, d, e" * echo $a->sub(3, -2); # "d, e" * echo $a->sub(-3, -1); # "d, e, f" * @endcode */ function sub($from, $to = null) { if(func_num_args() == 1) { $b = array_slice($this->a, $from); } else { $c = count($this->a); if($from < 0) $from += $c; if($to < 0) $to += $c; $b = array_slice($this->a, $from, abs($to - $from + 1)); } return $this->_new(array_values($b)); } /** * Removes and optionaly replaces specific elements from the array. * $offset and $len as in http://www.php.net/array_splice * $ary can be _array object or php array. * * @code * $a = _array(0, 1, 2, 3, 4, 5, 6); * echo $a->splice(3, 3, array('foo', 'bar')); # "0, 1, 2, foo, bar, 6" * @endcode */ function splice($offset, $len = null, $ary = null) { $b = $this->a; switch(func_num_args()) { case 1: array_splice($b, $offset); break; case 2: array_splice($b, $offset, $len); break; case 3: array_splice($b, $offset, $len, $this->_ary($ary)); break; } return $this->_new($b); } /** * Appends another array to the array. * * @code * $a = _array(1, 2, 3); * echo $a->append(_w('a b c')); # "1, 2, 3, a, b, c" * @endcode */ function append($ary) { $b = $this->a; array_splice($b, count($b), 0, $this->_ary($ary)); return $this->_new($b); } /** * Inserts another array before n-th element, shifting the rest towards the end. * * @code * $a = _array(1, 2, 3); * echo $a->insert(_w('a b c'), 1); # "1, a, b, c, 2, 3" * echo $a->insert(_w('a b c'), -1); # "1, 2, a, b, c, 3" * @endcode */ function insert($ary, $pos = 0) { $b = $this->a; array_splice($b, $pos, 0, $this->_ary($ary)); return $this->_new($b); } /* @}*/ /**@name Modificators (destructive functions). * @{ */ /** * Takes a variable number of parameters and appends them to the end of the array. * * @code * echo _array(1, 2, 3)->push(4, 5, 6); # "1, 2, 3, 4, 5, 6" * @endcode */ function push() { $_ = func_get_args(); array_splice($this->a, count($this->a), 0, $_); return $this; } /** * Takes a variable number of parameters and adds them to the beginning of the array. * * @code * echo _array(1, 2, 3)->unshift(4, 5, 6); # "4, 5, 6, 1, 2, 3" * @endcode */ function unshift() { $_ = func_get_args(); array_splice($this->a, 0, 0, $_); return $this; } /** * Removes and returns the last element. * * @code * echo _w('a b c')->pop(); # "c" * @endcode */ function pop() { return array_pop($this->a); } /** * Removes and returns the first element. * * @code * echo _w('a b c')->shift(); # "a" * @endcode */ function shift() { return array_shift($this->a); } /* @}*/ /**@name Set operations. * @{ */ /** * Computes the union of this array with other objects. * Takes a variable number of parameters which can be scalars, php arrays or _array objects. * Adds all parameters to this array. * * @code * $a = _w('foo bar'); * echo $a->union('baz', array('foo', 'quux')); # "foo, bar, baz, quux" * @endcode */ function union() { $_ = func_get_args(); return $this->_gcall('array_merge', $_)->unique(); } /** * Computes the intersection of this array with other objects. * Takes a variable number of parameters which can be scalars, php arrays or _array objects. * Remove elements of this array that don't exist in all parameters. * * @code * $a = _w('foo bar baz'); * echo $a->intersect(array('foo', 'quux'), _w('foo baz')); # "foo" * @endcode */ function intersect() { $_ = func_get_args(); return $this->_gcall('array_intersect', $_); } /** * Computes the difference between this array and other objects. * Takes a variable number of parameters which can be scalars, php arrays or _array objects. * Remove elements of this array that exist in any of parameters. * * @code * $a = _w('foo bar baz'); * echo $a->diff(array('foo', 'quux'), _w('foo baz')); # "bar" * @endcode */ function diff() { $_ = func_get_args(); return $this->_gcall('array_diff', $_); } /* @}*/ /**@name High-order (arrays of arrays) functions. * @{ */ /** * Merges multiple arrays together. * Takes a variable number of parameters which can be php arrays or _array objects * and merges elements of this array with corresponding elements from each parameter. * The result will be array of arrays. * * @code * $a = _w('1 2 3'); * $b = $a->zip(_w('4 5 6'), _w('7 8')); * echo $b->flatten(); # "1, 4, 7, 2, 5, 8, 3, 6" * @endcode */ function zip() { $_ = func_get_args(); $_ = array_map(array($this, '_ary'), $_); $b = array(); foreach($this->a as $k => $v) { $q = array($v); foreach($_ as $arg) if(isset($arg[$k])) $q[] = $arg[$k]; $b[$k] = $q; } return $this->_new($b); } /** * Collects all elements with specific key. * The source array is assumed to be array of arrays. * * @code * $a = _array(_w('a b c'), _w('A B C'), _w('0 1 2')); * echo $a->pluck(1); # "b, B, 1" * @endcode */ function pluck($key) { $b = array(); foreach($this->a as $v) { if(($v instanceof self) && isset($v->a[$key])) $b[] = $v->a[$key]; else if(is_array($v) && isset($v[$key])) $b[] = $v[$key]; } return $this->_new($b); } /** * Combines this array with another array. * The values of this are treated as keys for the new array * and the values of the parameter are values. * Arrays can be of different length, missing keys are substituted with numbers, * missing values are set to null. * * @code * $a = _w('a b c')->combine(_w('1 2 3')); * echo $a->keys() . '+' . $a->values(); # "a, b, c+1, 2, 3" * @endcode */ function combine($values) { $b = array(); $values = $this->_ary($values); foreach($this->a as $key) $b[$key] = array_shift($values); if(count($values)) $b = array_merge($b, $values); return $this->_new($b); } /** * Recursively flattens a nested array. * * @code * $a = array(1, 22, 'sub' => array(333, 'deep' => 4444)); * echo _a($a)->flatten(); # "1, 22, 333, 4444" * @endcode */ function flatten() { $b = $this->a; return $this->_new($this->_flatten($b)); } /* @}*/ /**@name Array-to-string convertors. * @{ */ /** * Packs elements into a binary string. * Format specifiers see http://php.net/pack * * @code * echo _array(0x1234, 0x5678, 65, 66)->pack("nvc*"); # "\x12\x34\x78\x56AB" * @endcode */ function pack($format) { $b = $this->a; array_unshift($b, $format); return call_user_func_array('pack', $b); } /** * Converts array to a string separated by $delimiter. * * @code * echo _w('a b c')->join("/"); # "a/b/c" * @endcode */ function join($delim = '') { return implode($delim, array_map('strval', $this->a)); } /* @}*/ /**@name Miscellaneous. * @{ */ /** * Counts the values of the array. * Returns an _array object populated by the result of http://www.php.net/array_count_values. * If recurse is set, also handles subarrays. * * @code * echo _w('a a a b b c')->stat()->item('b'); # 2 * @endcode */ function stat($recurse = false) { if($recurse) { $stat = array(); $this->_stat($this->a, $stat); } else $stat = array_count_values($this->a); return $this->_new($stat); } /** * Removes duplicate elements from the array. * * @code * echo _w('a a b b c')->unique(); # "a, b, c" * @endcode */ function unique() { return $this->_new(array_unique($this->a)); } /** * Reverses the array. * * @code * echo _w('1 2 3')->reverse(); # "3, 2, 1" * @endcode */ function reverse($preserve_keys = false) { return $this->_new(array_reverse($this->a, $preserve_keys)); } /** * Exchanges array keys with values. * * @code * echo _w('a b c')->flip()->keys(); # "a, b, c" * @endcode */ function flip() { return $this->_new(array_flip($this->a)); } /** * Returns a dump of the array (print_r). * * @code * echo _w('a b c')->inspect(); ## * @endcode */ function inspect() { return print_r($this->a, true); } /* @}*/ protected $a = array(); protected function _new($ary) { $a = new self; $a->a = $ary; return $a; } protected function _stat($a, &$stat) { foreach($a as $e) { if(is_array($e)) $this->_stat($e, $stat); else if($e instanceof self) $this->_stat($e->a, $stat); else if(isset($stat[$e])) $stat[$e]++; else $stat[$e] = 1; } } protected function _count($a) { $c = 0; foreach($a as $e) { if(is_array($e)) $c += $this->_count($e); else if($e instanceof self) $c += $this->_count($e->a); else $c++; } return $c; } protected function _flatten($a) { $b = array(); foreach($a as $e) { if(is_array($e)) $b = array_merge($b, $this->_flatten($e)); else if($e instanceof self) $b = array_merge($b, $this->_flatten($e->values())); else $b[] = $e; } return $b; } protected function _gcall($func, $args) { $b = array_map(array($this, '_ary'), $args); array_unshift($b, $this->a); return $this->_new(call_user_func_array($func, $b)); } protected function _rmap($a, $args) { if($a instanceof self || is_array($a)) { $b = array(); foreach($a as $k => $v) $b[$k] = $this->_rmap($a[$k], $args); return $b; } $f = $args[0]; $args[0] = $a; return call_user_func_array($f, $args); } protected function _filter($a, $args) { $c = count($args); if($c == 0) return array_filter($a); if($c == 1) return array_filter($a, $args[0]); $b = array(); $func = $args[0]; foreach($a as $k => $v) { $args[0] = $v; if(call_user_func_array($func, $args)) $b[$k] = $v; } return $b; } protected function _ary($ary) { return $ary instanceof self ? $ary->a() : (is_array($ary) ? $ary : array($ary)); } } ?>