ArrayUtils.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. /**
  3. * Methods to play with arrays.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program 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
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. */
  22. /**
  23. * A collection of static methods to play with arrays.
  24. *
  25. * @since 1.21
  26. */
  27. class ArrayUtils {
  28. /**
  29. * Sort the given array in a pseudo-random order which depends only on the
  30. * given key and each element value in $array. This is typically used for load
  31. * balancing between servers each with a local cache.
  32. *
  33. * Keys are preserved. The input array is modified in place.
  34. *
  35. * Note: Benchmarking on PHP 5.3 and 5.4 indicates that for small
  36. * strings, md5() is only 10% slower than hash('joaat',...) etc.,
  37. * since the function call overhead dominates. So there's not much
  38. * justification for breaking compatibility with installations
  39. * compiled with ./configure --disable-hash.
  40. *
  41. * @param array &$array Array to sort
  42. * @param string $key
  43. * @param string $separator A separator used to delimit the array elements and the
  44. * key. This can be chosen to provide backwards compatibility with
  45. * various consistent hash implementations that existed before this
  46. * function was introduced.
  47. */
  48. public static function consistentHashSort( &$array, $key, $separator = "\000" ) {
  49. $hashes = [];
  50. foreach ( $array as $elt ) {
  51. $hashes[$elt] = md5( $elt . $separator . $key );
  52. }
  53. uasort( $array, function ( $a, $b ) use ( $hashes ) {
  54. return strcmp( $hashes[$a], $hashes[$b] );
  55. } );
  56. }
  57. /**
  58. * Given an array of non-normalised probabilities, this function will select
  59. * an element and return the appropriate key
  60. *
  61. * @param array $weights
  62. * @return bool|int|string
  63. */
  64. public static function pickRandom( $weights ) {
  65. if ( !is_array( $weights ) || count( $weights ) == 0 ) {
  66. return false;
  67. }
  68. $sum = array_sum( $weights );
  69. if ( $sum == 0 ) {
  70. # No loads on any of them
  71. # In previous versions, this triggered an unweighted random selection,
  72. # but this feature has been removed as of April 2006 to allow for strict
  73. # separation of query groups.
  74. return false;
  75. }
  76. $max = mt_getrandmax();
  77. $rand = mt_rand( 0, $max ) / $max * $sum;
  78. $sum = 0;
  79. foreach ( $weights as $i => $w ) {
  80. $sum += $w;
  81. # Do not return keys if they have 0 weight.
  82. # Note that the "all 0 weight" case is handed above
  83. if ( $w > 0 && $sum >= $rand ) {
  84. break;
  85. }
  86. }
  87. return $i;
  88. }
  89. /**
  90. * Do a binary search, and return the index of the largest item that sorts
  91. * less than or equal to the target value.
  92. *
  93. * @since 1.23
  94. *
  95. * @param callable $valueCallback A function to call to get the value with
  96. * a given array index.
  97. * @param int $valueCount The number of items accessible via $valueCallback,
  98. * indexed from 0 to $valueCount - 1
  99. * @param callable $comparisonCallback A callback to compare two values, returning
  100. * -1, 0 or 1 in the style of strcmp().
  101. * @param string $target The target value to find.
  102. *
  103. * @return int|bool The item index of the lower bound, or false if the target value
  104. * sorts before all items.
  105. */
  106. public static function findLowerBound( $valueCallback, $valueCount,
  107. $comparisonCallback, $target
  108. ) {
  109. if ( $valueCount === 0 ) {
  110. return false;
  111. }
  112. $min = 0;
  113. $max = $valueCount;
  114. do {
  115. $mid = $min + ( ( $max - $min ) >> 1 );
  116. $item = $valueCallback( $mid );
  117. $comparison = $comparisonCallback( $target, $item );
  118. if ( $comparison > 0 ) {
  119. $min = $mid;
  120. } elseif ( $comparison == 0 ) {
  121. $min = $mid;
  122. break;
  123. } else {
  124. $max = $mid;
  125. }
  126. } while ( $min < $max - 1 );
  127. if ( $min == 0 ) {
  128. $item = $valueCallback( $min );
  129. $comparison = $comparisonCallback( $target, $item );
  130. if ( $comparison < 0 ) {
  131. // Before the first item
  132. return false;
  133. }
  134. }
  135. return $min;
  136. }
  137. /**
  138. * Do array_diff_assoc() on multi-dimensional arrays.
  139. *
  140. * Note: empty arrays are removed.
  141. *
  142. * @since 1.23
  143. *
  144. * @param array $array1 The array to compare from
  145. * @param array ...$arrays More arrays to compare against
  146. * @return array An array containing all the values from array1
  147. * that are not present in any of the other arrays.
  148. */
  149. public static function arrayDiffAssocRecursive( $array1, ...$arrays ) {
  150. $ret = [];
  151. foreach ( $array1 as $key => $value ) {
  152. if ( is_array( $value ) ) {
  153. $args = [ $value ];
  154. foreach ( $arrays as $array ) {
  155. if ( isset( $array[$key] ) ) {
  156. $args[] = $array[$key];
  157. }
  158. }
  159. $valueret = self::arrayDiffAssocRecursive( ...$args );
  160. if ( count( $valueret ) ) {
  161. $ret[$key] = $valueret;
  162. }
  163. } else {
  164. foreach ( $arrays as $array ) {
  165. if ( isset( $array[$key] ) && $array[$key] === $value ) {
  166. continue 2;
  167. }
  168. }
  169. $ret[$key] = $value;
  170. }
  171. }
  172. return $ret;
  173. }
  174. }