MediaWiki master
ArrayUtils.php
Go to the documentation of this file.
1<?php
10
11use function array_flip;
12use function array_key_exists;
13use function array_keys;
14use function array_slice;
15use function count;
16
43 public static function consistentHashSort( &$array, $key, $separator = "\000" ) {
44 $hashes = [];
45 foreach ( $array as $elt ) {
46 $hashes[$elt] = md5( $elt . $separator . $key );
47 }
48 uasort( $array, static function ( $a, $b ) use ( $hashes ) {
49 return strcmp( $hashes[$a], $hashes[$b] );
50 } );
51 }
52
60 public static function pickRandom( $weights ) {
61 if ( !is_array( $weights ) || count( $weights ) == 0 ) {
62 return false;
63 }
64
65 $sum = array_sum( $weights );
66 if ( $sum == 0 ) {
67 # No loads on any of them
68 # In previous versions, this triggered an unweighted random selection,
69 # but this feature has been removed as of April 2006 to allow for strict
70 # separation of query groups.
71 return false;
72 }
73 $max = mt_getrandmax();
74 $rand = mt_rand( 0, $max ) / $max * $sum;
75
76 $sum = 0;
77 foreach ( $weights as $i => $w ) {
78 $sum += $w;
79 # Do not return keys if they have 0 weight.
80 # Note that the "all 0 weight" case is handed above
81 if ( $w > 0 && $sum >= $rand ) {
82 break;
83 }
84 }
85
86 return $i;
87 }
88
106 public static function findLowerBound( $valueCallback, $valueCount,
107 $comparisonCallback, $target
108 ) {
109 if ( $valueCount === 0 ) {
110 return false;
111 }
112
113 $min = 0;
114 $max = $valueCount;
115 do {
116 $mid = $min + ( ( $max - $min ) >> 1 );
117 $item = $valueCallback( $mid );
118 $comparison = $comparisonCallback( $target, $item );
119 if ( $comparison > 0 ) {
120 $min = $mid;
121 } elseif ( $comparison == 0 ) {
122 $min = $mid;
123 break;
124 } else {
125 $max = $mid;
126 }
127 } while ( $min < $max - 1 );
128
129 if ( $min == 0 ) {
130 $item = $valueCallback( $min );
131 $comparison = $comparisonCallback( $target, $item );
132 if ( $comparison < 0 ) {
133 // Before the first item
134 return false;
135 }
136 }
137 return $min;
138 }
139
152 public static function arrayDiffAssocRecursive( $array1, ...$arrays ) {
153 $ret = [];
154
155 foreach ( $array1 as $key => $value ) {
156 if ( is_array( $value ) ) {
157 $args = [ $value ];
158 foreach ( $arrays as $array ) {
159 if ( isset( $array[$key] ) ) {
160 $args[] = $array[$key];
161 }
162 }
163 $valueret = self::arrayDiffAssocRecursive( ...$args );
164 if ( count( $valueret ) ) {
165 $ret[$key] = $valueret;
166 }
167 } else {
168 foreach ( $arrays as $array ) {
169 if ( isset( $array[$key] ) && $array[$key] === $value ) {
170 continue 2;
171 }
172 }
173 $ret[$key] = $value;
174 }
175 }
176
177 return $ret;
178 }
179
204 public static function cartesianProduct( ...$inputArrays ) {
205 $numInputs = count( $inputArrays );
206 if ( $numInputs === 0 ) {
207 return [];
208 }
209
210 // Reset the internal pointers
211 foreach ( $inputArrays as &$inputArray ) {
212 if ( !count( $inputArray ) ) {
213 return [];
214 }
215 reset( $inputArray );
216 }
217 unset( $inputArray );
218
219 $outputArrays = [];
220 $done = false;
221 while ( !$done ) {
222 // Construct the output array element
223 $element = [];
224 foreach ( $inputArrays as $inputArray ) {
225 $element[] = current( $inputArray );
226 }
227 $outputArrays[] = $element;
228
229 // Increment the pointers starting from the least significant.
230 // If the least significant rolls over back to the start of the
231 // array, continue with the next most significant, and so on until
232 // that stops happening. If all pointers roll over, we are done.
233 $done = true;
234 for ( $paramIndex = $numInputs - 1; $paramIndex >= 0; $paramIndex-- ) {
235 next( $inputArrays[$paramIndex] );
236 if ( key( $inputArrays[$paramIndex] ) === null ) {
237 reset( $inputArrays[$paramIndex] );
238 // continue
239 } else {
240 $done = false;
241 break;
242 }
243 }
244 }
245 return $outputArrays;
246 }
247
260 public static function arrayPlus2d( array $baseArray, array $newValues ): array {
261 // First merge items that are in both arrays
262 foreach ( $baseArray as $name => &$groupVal ) {
263 if ( isset( $newValues[$name] ) ) {
264 $groupVal += $newValues[$name];
265 }
266 }
267 // Now add items that didn't exist yet
268 $baseArray += $newValues;
269
270 return $baseArray;
271 }
272
283 public static function insertAfter( array $array, array $insert, string|int $after ): array {
284 // Find the offset of the element to insert after.
285 $keys = array_keys( $array );
286 $offsetByKey = array_flip( $keys );
287
288 if ( !array_key_exists( $after, $offsetByKey ) ) {
289 return $array;
290 }
291 $offset = $offsetByKey[$after];
292
293 // Insert at the specified offset
294 $before = array_slice( $array, 0, $offset + 1, true );
295 $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
296
297 $output = $before + $insert + $after;
298
299 return $output;
300 }
301}
302
304class_alias( ArrayUtils::class, 'ArrayUtils' );
A collection of static methods to play with arrays.
static findLowerBound( $valueCallback, $valueCount, $comparisonCallback, $target)
Do a binary search, and return the index of the largest item that sorts less than or equal to the tar...
static arrayPlus2d(array $baseArray, array $newValues)
Merges two (possibly) 2 dimensional arrays into the target array ($baseArray).
static consistentHashSort(&$array, $key, $separator="\000")
Sort the given array in a pseudo-random order which depends only on the given key and each element va...
static pickRandom( $weights)
Given an array of non-normalised probabilities, this function will select an element and return the a...
static insertAfter(array $array, array $insert, string|int $after)
Insert an array into another array after the specified key.
static cartesianProduct(... $inputArrays)
Make an array consisting of every combination of the elements of the input arrays.
static arrayDiffAssocRecursive( $array1,... $arrays)
Do array_diff_assoc() on multi-dimensional arrays.