1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Class Arrays |
||
5 | * |
||
6 | * @link https://www.icy2003.com/ |
||
7 | * @author icy2003 <[email protected]> |
||
8 | * @copyright Copyright (c) 2017, icy2003 |
||
9 | */ |
||
10 | |||
11 | namespace icy2003\php\ihelpers; |
||
12 | |||
13 | use icy2003\php\C; |
||
14 | use icy2003\php\I; |
||
15 | |||
16 | /** |
||
17 | * 数组类 |
||
18 | * |
||
19 | * 常见数组格式的拼装和处理 |
||
20 | * |
||
21 | * @test icy2003\php_tests\ihelpers\ArraysTest |
||
22 | */ |
||
23 | class Arrays |
||
24 | { |
||
25 | |||
26 | /** |
||
27 | * 以各个元素的某字段值作为键重新指回该元素,此值对于该元素需唯一 |
||
28 | * |
||
29 | * @param array $array 元素可以为数组或者对象 |
||
30 | * @param string $index 用来作为键的某字段,不能为 null |
||
31 | * @param boolean $isMerge 是否合并相同键的项到数组,默认否(也就是后者覆盖前者) |
||
32 | * |
||
33 | * @return array |
||
34 | * |
||
35 | * @tested |
||
36 | */ |
||
37 | 1 | public static function indexBy($array, $index, $isMerge = false) |
|
38 | { |
||
39 | 1 | $result = []; |
|
40 | 1 | foreach ($array as $row) { |
|
41 | 1 | $indexValue = I::get($row, $index); |
|
42 | 1 | if (null === $indexValue) { |
|
43 | 1 | return []; |
|
44 | } |
||
45 | 1 | if (false === $isMerge) { |
|
46 | 1 | $result[$indexValue] = $row; |
|
47 | } else { |
||
48 | 1 | $result[$indexValue][] = $row; |
|
49 | } |
||
50 | } |
||
51 | |||
52 | 1 | return $result; |
|
53 | } |
||
54 | |||
55 | /** |
||
56 | * 选取数组中指定键的某几列 |
||
57 | * |
||
58 | * ``` |
||
59 | * - 简单理解就是:从数据库里查出来几条数据,只拿其中的几个属性 |
||
60 | * - 当 $dimension 为 2 时,理解为从几条数据里拿属性 |
||
61 | * - 当 $dimension 为 1 时,理解为从一条数据里拿属性 |
||
62 | * ``` |
||
63 | * |
||
64 | * @param array $array |
||
65 | * @param array $keys 某几项字段,支持 I::get 的键格式,如果是键值对,键会被设置为键 |
||
66 | * - [a, b]:查找 a 和 b |
||
67 | * - [a.b, c]:查找 a.b 和 c |
||
68 | * - [a => b]:查找 b 并且设置该项的键为 a |
||
69 | * @param integer $dimension 维度,只能为 1 或 2,默认 2,表示处理二维数组 |
||
70 | * |
||
71 | * @return array |
||
72 | * |
||
73 | * @tested |
||
74 | */ |
||
75 | 1 | public static function columns($array, $keys = null, $dimension = 2) |
|
76 | { |
||
77 | 1 | if (null === $keys) { |
|
78 | 1 | return $array; |
|
79 | } |
||
80 | 1 | $result = []; |
|
81 | 1 | if (2 === $dimension) { |
|
82 | 1 | foreach ($array as $k => $row) { |
|
83 | 1 | foreach ($keys as $k1 => $key) { |
|
84 | 1 | $value = I::get($row, $key); |
|
85 | 1 | if (is_numeric($k1)) { |
|
86 | 1 | $result[$k][$key] = $value; |
|
87 | } else { |
||
88 | $result[$k][$k1] = $value; |
||
89 | } |
||
90 | } |
||
91 | } |
||
92 | } |
||
93 | 1 | if (1 === $dimension) { |
|
94 | 1 | foreach ($keys as $k1 => $key) { |
|
95 | 1 | $value = I::get($array, $key); |
|
96 | 1 | if (is_numeric($k1)) { |
|
97 | 1 | $result[$key] = $value; |
|
98 | } else { |
||
99 | $result[$k1] = $value; |
||
100 | } |
||
101 | } |
||
102 | } |
||
103 | |||
104 | 1 | return $result; |
|
105 | } |
||
106 | |||
107 | /** |
||
108 | * Arrays::columns 第三参数取值 1 |
||
109 | * @ignore |
||
110 | */ |
||
111 | public static function columns1($array, $keys = null) |
||
112 | { |
||
113 | return self::columns($array, $keys, 1); |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * Arrays::columns 第三参数取值 2 |
||
118 | * @ignore |
||
119 | */ |
||
120 | public static function columns2($array, $keys = null) |
||
121 | { |
||
122 | return self::columns($array, $keys, 2); |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * 返回二维(或者更高)数组中指定键的一列的所有值 |
||
127 | * |
||
128 | * @see http://php.net/array_column |
||
129 | * |
||
130 | * ``` |
||
131 | * - 在不传入 $index 时,键将原样保持,这里已经和 array_column 功能不一致了 |
||
132 | * - 如果需要取某几项,使用 Arrays::columns |
||
133 | * - 简单理解就是:从数据库里查出来几条数据,只要其中某个属性的所有值 |
||
134 | * ``` |
||
135 | * |
||
136 | * @param array $array |
||
137 | * @param string $column 需要被取出来的字段 |
||
138 | * @param string $index 作为 index 的字段 |
||
139 | * |
||
140 | * @return array |
||
141 | * |
||
142 | * @tested |
||
143 | */ |
||
144 | 2 | public static function column($array, $column, $index = null) |
|
145 | { |
||
146 | 2 | $result = []; |
|
147 | 2 | foreach ($array as $key => $row) { |
|
148 | 2 | $data = I::get($row, $column); |
|
149 | 2 | if (null === $index) { |
|
150 | 2 | $result[$key] = $data; |
|
151 | } else { |
||
152 | 1 | $result[I::get($row, $index)] = $data; |
|
153 | } |
||
154 | } |
||
155 | |||
156 | 2 | return $result; |
|
157 | } |
||
158 | |||
159 | /** |
||
160 | * 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值 |
||
161 | * |
||
162 | * ``` |
||
163 | * - array_combine:两个数组元素个数不一致将报错 |
||
164 | * - 在两个数组元素个数不一致时,以键为准: |
||
165 | * 1.键比值多,值都被填充为 null |
||
166 | * 2.值比键多,值被舍去 |
||
167 | * ``` |
||
168 | * |
||
169 | * @see http://php.net/array_combine |
||
170 | * |
||
171 | * @param array $keys |
||
172 | * @param array $values |
||
173 | * |
||
174 | * @return array |
||
175 | * |
||
176 | * @tested |
||
177 | */ |
||
178 | 2 | public static function combine($keys, $values) |
|
179 | { |
||
180 | 2 | if (count($keys) == count($values)) { |
|
181 | 2 | return (array) array_combine($keys, $values); |
|
182 | } |
||
183 | 1 | $array = []; |
|
184 | 1 | foreach ($keys as $index => $key) { |
|
185 | 1 | $array[$key] = I::get($values, $index); |
|
186 | } |
||
187 | 1 | return $array; |
|
188 | } |
||
189 | |||
190 | /** |
||
191 | * 递归地合并多个数组 |
||
192 | * |
||
193 | * ``` |
||
194 | * - array_merge_recursive:如果有相同的键,后者会覆盖前者 |
||
195 | * - 此函数会合并两个相同键的值到一个数组里 |
||
196 | * ``` |
||
197 | * |
||
198 | * @see http://php.net/array_merge_recursive |
||
199 | * |
||
200 | * @param array $a 数组1 |
||
201 | * @param array $b 数组2(可以任意个数组) |
||
202 | * |
||
203 | * @return array |
||
204 | * |
||
205 | * @tested |
||
206 | */ |
||
207 | 37 | public static function merge($a, $b) |
|
208 | { |
||
209 | 37 | $args = func_get_args(); |
|
210 | 37 | $res = array_shift($args); |
|
211 | 37 | while (!empty($args)) { |
|
212 | 37 | foreach (array_shift($args) as $k => $v) { |
|
213 | 3 | if (is_int($k)) { |
|
214 | 2 | if (array_key_exists($k, $res)) { |
|
215 | 2 | $res[] = $v; |
|
216 | } else { |
||
217 | 2 | $res[$k] = $v; |
|
218 | } |
||
219 | 2 | } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) { |
|
220 | 1 | $res[$k] = self::merge($res[$k], $v); |
|
221 | } else { |
||
222 | 2 | $res[$k] = $v; |
|
223 | } |
||
224 | } |
||
225 | } |
||
226 | |||
227 | 37 | return $res; |
|
228 | } |
||
229 | |||
230 | /** |
||
231 | * range 的性能优化版 |
||
232 | * |
||
233 | * @see http://php.net/manual/zh/language.generators.overview.php |
||
234 | * @version PHP >= 5.5 |
||
235 | * |
||
236 | * @param integer $start 开始 |
||
237 | * @param integer $end 结束 |
||
238 | * @param integer $step 步长 |
||
239 | * |
||
240 | * @return \Generator |
||
241 | * |
||
242 | * @tested |
||
243 | */ |
||
244 | 8 | public static function rangeGenerator($start, $end, $step = 1) |
|
245 | { |
||
246 | 8 | if ($start < $end) { |
|
247 | 7 | C::assertTrue($step > 0, '步长必须大于 0'); |
|
248 | 7 | for ($i = $start; $i <= $end; $i += $step) { |
|
249 | 7 | yield $i; |
|
250 | } |
||
251 | 3 | } elseif ($start > $end) { |
|
252 | 1 | C::assertTrue($step < 0, '步长必须小于 0'); |
|
253 | 1 | for ($i = $start; $i >= $end; $i += $step) { |
|
254 | 1 | yield $i; |
|
255 | } |
||
256 | } else { |
||
257 | 2 | yield $start; |
|
258 | } |
||
259 | 8 | } |
|
260 | |||
261 | /** |
||
262 | * 找到符合条件的第一项 |
||
263 | * |
||
264 | * @param array $array |
||
265 | * @param callback $callback 条件回调,结果为 true 的第一项会被取出 |
||
266 | * |
||
267 | * @return mixed |
||
268 | * |
||
269 | * @tested |
||
270 | */ |
||
271 | 1 | public static function detectFirst($array, $callback) |
|
272 | { |
||
273 | 1 | foreach ($array as $key => $item) { |
|
274 | 1 | if (true === I::call($callback, [$item, $key])) { |
|
275 | 1 | return $item; |
|
276 | } |
||
277 | } |
||
278 | 1 | return null; |
|
279 | } |
||
280 | |||
281 | /** |
||
282 | * 找到符合条件的所有项 |
||
283 | * |
||
284 | * @param array $array |
||
285 | * @param callback $callback 条件回调,结果为 true 的所有项会被取出 |
||
286 | * @param callback $filter 对符合条件的项进行回调处理并返回 |
||
287 | * |
||
288 | * @return array |
||
289 | * |
||
290 | * @tested |
||
291 | */ |
||
292 | 1 | public static function detectAll($array, $callback, $filter = null) |
|
293 | { |
||
294 | 1 | $all = []; |
|
295 | 1 | foreach ($array as $key => $item) { |
|
296 | 1 | if (true === I::call($callback, [$item, $key])) { |
|
297 | 1 | if (null !== $filter) { |
|
298 | 1 | $all[$key] = I::call($filter, [$item, $key]); |
|
299 | } else { |
||
300 | 1 | $all[$key] = $item; |
|
301 | } |
||
302 | } |
||
303 | } |
||
304 | 1 | return $all; |
|
305 | } |
||
306 | |||
307 | /** |
||
308 | * 返回数组的最后一个元素的键 |
||
309 | * |
||
310 | * - array_key_last:需要 PHP7.3.0+ 才能支持 |
||
311 | * - USE_CUSTOM |
||
312 | * |
||
313 | * @param array $array |
||
314 | * |
||
315 | * @return string|null |
||
316 | * |
||
317 | * @tested |
||
318 | */ |
||
319 | 1 | public static function keyLast($array) |
|
320 | { |
||
321 | 1 | if (!is_array($array) || empty($array)) { |
|
322 | 1 | return null; |
|
323 | } |
||
324 | 1 | if (function_exists('array_key_last') && false === I::ini('USE_CUSTOM')) { |
|
325 | 1 | return array_key_last($array); |
|
326 | } |
||
327 | 1 | end($array); |
|
328 | 1 | return key($array); |
|
329 | } |
||
330 | |||
331 | /** |
||
332 | * 返回数组的第一个元素的键 |
||
333 | * |
||
334 | * - array_key_first:需要 PHP7.3.0+ 才能支持 |
||
335 | * - USE_CUSTOM |
||
336 | * |
||
337 | * @param array $array |
||
338 | * |
||
339 | * @return string|null |
||
340 | * |
||
341 | * @tested |
||
342 | */ |
||
343 | 1 | public static function keyFirst($array) |
|
344 | { |
||
345 | 1 | if (!is_array($array) || empty($array)) { |
|
346 | 1 | return null; |
|
347 | } |
||
348 | 1 | if (function_exists('array_key_first') && false === I::ini('USE_CUSTOM')) { |
|
349 | 1 | return array_key_first($array); |
|
350 | } |
||
351 | 1 | reset($array); |
|
352 | 1 | return key($array); |
|
353 | } |
||
354 | |||
355 | /** |
||
356 | * 获取数组的维度 |
||
357 | * |
||
358 | * @param array $array 多维数组 |
||
359 | * |
||
360 | * @return integer |
||
361 | * |
||
362 | * @tested |
||
363 | */ |
||
364 | 1 | public static function dimension($array) |
|
365 | { |
||
366 | 1 | if (!is_array($array)) { |
|
367 | 1 | return 0; |
|
368 | } |
||
369 | 1 | $max = 1; |
|
370 | 1 | foreach ($array as $value) { |
|
371 | 1 | if (is_array($value)) { |
|
372 | 1 | $d = self::dimension($value) + 1; |
|
373 | 1 | if ($d > $max) { |
|
374 | 1 | $max = $d; |
|
375 | } |
||
376 | } |
||
377 | } |
||
378 | 1 | return $max; |
|
379 | } |
||
380 | |||
381 | /** |
||
382 | * 判断数组是不是关联数组 |
||
383 | * |
||
384 | * @param array $array |
||
385 | * |
||
386 | * @return boolean |
||
387 | * |
||
388 | * @tested |
||
389 | */ |
||
390 | 1 | public static function isAssoc($array) |
|
391 | { |
||
392 | 1 | if (is_array($array)) { |
|
393 | 1 | $keys = array_keys($array); |
|
394 | 1 | return $keys !== array_keys($keys); |
|
395 | } |
||
396 | 1 | return false; |
|
397 | } |
||
398 | |||
399 | /** |
||
400 | * 判断数组是不是索引数组 |
||
401 | * |
||
402 | * 索引数组必须是下标从 0 开始的数组,键是数字还是字符串类型的数字无所谓 |
||
403 | * |
||
404 | * @param array $array |
||
405 | * |
||
406 | * @return boolean |
||
407 | * |
||
408 | * @tested |
||
409 | */ |
||
410 | 1 | public static function isIndexed($array) |
|
411 | { |
||
412 | 1 | if (is_array($array)) { |
|
413 | 1 | $keys = array_keys($array); |
|
414 | 1 | return $keys === array_keys($keys); |
|
415 | } |
||
416 | 1 | return false; |
|
417 | } |
||
418 | |||
419 | /** |
||
420 | * 返回数组的顺数第 n 个元素,其中 n >= 1 且为整数,空数组直接返回 null |
||
421 | * |
||
422 | * - 支持关联数组,超过数组长度会对数组长度求余后查找 |
||
423 | * |
||
424 | * @param array $array |
||
425 | * @param int $pos 顺数第 n 个,默认 1 |
||
426 | * |
||
427 | * @return mixed |
||
428 | * |
||
429 | * @tested |
||
430 | */ |
||
431 | 3 | public static function first($array, $pos = 1) |
|
432 | { |
||
433 | 3 | if (0 === ($count = self::count($array))) { |
|
434 | 1 | return null; |
|
435 | } |
||
436 | 3 | $p = $pos % $count; |
|
437 | 3 | if (0 === $p) { |
|
438 | 2 | $p = $count; |
|
439 | } |
||
440 | 3 | for ($i = 1; $i < $p; $i++) { |
|
441 | 1 | next($array); |
|
442 | } |
||
443 | 3 | return current($array); |
|
444 | } |
||
445 | |||
446 | /** |
||
447 | * 返回数组的倒数第 n 个元素,其中 n >= 1 且为整数,空数组直接返回 null |
||
448 | * |
||
449 | * - 支持关联数组,超过数组长度会对数组长度求余后查找 |
||
450 | * |
||
451 | * @param array $array |
||
452 | * @param int $pos 倒数第 n 个,默认 1 |
||
453 | * |
||
454 | * @return mixed |
||
455 | * |
||
456 | * @tested |
||
457 | */ |
||
458 | 1 | public static function last($array, $pos = 1) |
|
459 | { |
||
460 | 1 | if (0 === ($count = self::count($array))) { |
|
461 | 1 | return null; |
|
462 | } |
||
463 | 1 | $p = $pos % $count; |
|
464 | 1 | if (0 === $p) { |
|
465 | 1 | $p = $count; |
|
466 | } |
||
467 | 1 | end($array); |
|
468 | 1 | for ($i = 1; $i < $p; $i++) { |
|
469 | 1 | prev($array); |
|
470 | } |
||
471 | 1 | return current($array); |
|
472 | } |
||
473 | |||
474 | /** |
||
475 | * 计算数组中的单元数目 |
||
476 | * |
||
477 | * - count:在非数组情况下,除了 null 会返回 0,其他都返回 1,囧 |
||
478 | * - $callback 参数用于对符合条件的项做筛选 |
||
479 | * |
||
480 | * @param array|mixed $array 数组 |
||
481 | * @param callback|mixed $callback 回调,返回回调值为 true 的项,如果此参数是非回调类型,表示查询和此值严格相等的项 |
||
482 | * @param boolean $isStrict 是否为严格模式,如果为 false,回调值为 true 值的也会返回,为字符串时不使用严格比较 |
||
483 | * |
||
484 | * @return integer |
||
485 | * |
||
486 | * @tested |
||
487 | */ |
||
488 | 10 | public static function count($array, $callback = null, $isStrict = true) |
|
489 | { |
||
490 | 10 | $count = 0; |
|
491 | 10 | if (is_array($array)) { |
|
492 | 10 | if (null === $callback) { |
|
493 | 10 | return count($array); |
|
494 | } else { |
||
495 | 2 | $function = $callback; |
|
496 | 2 | if (false === is_callable($callback)) { |
|
497 | $function = function ($row) use ($callback, $isStrict) { |
||
498 | 2 | return true === $isStrict ? $row === $callback : $row == $callback; |
|
499 | 2 | }; |
|
500 | } |
||
501 | 2 | foreach ($array as $key => $row) { |
|
502 | 2 | if (true === I::call($function, [$row, $key])) { |
|
503 | 2 | $count++; |
|
504 | } |
||
505 | } |
||
506 | } |
||
507 | } |
||
508 | 2 | return $count; |
|
509 | } |
||
510 | |||
511 | /** |
||
512 | * 返回指定长度的数组,不足的值设置为 null |
||
513 | * |
||
514 | * @param array $array |
||
515 | * @param integer $count 指定长度 |
||
516 | * @param callback $callback 回调参数:数组的值、数组的键 |
||
517 | * |
||
518 | * @return array |
||
519 | * |
||
520 | * @tested |
||
521 | */ |
||
522 | 2 | public static function lists($array, $count = null, $callback = null) |
|
523 | { |
||
524 | 2 | null === $count && $count = self::count($array); |
|
525 | 2 | $arrayCount = self::count($array); |
|
526 | 2 | if ($arrayCount >= $count) { |
|
527 | 2 | $return = $array; |
|
528 | } else { |
||
529 | 1 | $return = self::merge($array, self::fill(0, $count - $arrayCount, null)); |
|
530 | } |
||
531 | 2 | if (null !== $callback) { |
|
532 | 1 | foreach ($return as $key => $value) { |
|
533 | 1 | $return[$key] = I::call($callback, [$value, $key]); |
|
534 | } |
||
535 | } |
||
536 | 2 | return $return; |
|
537 | } |
||
538 | |||
539 | /** |
||
540 | * 获取指定某些键的项的值 |
||
541 | * |
||
542 | * @param array $array |
||
543 | * @param array|string $keys 数组或逗号字符串 |
||
544 | * |
||
545 | * @return array |
||
546 | * |
||
547 | * @tested |
||
548 | */ |
||
549 | 1 | public static function values($array, $keys = null) |
|
550 | { |
||
551 | 1 | return array_values(self::some($array, $keys)); |
|
552 | } |
||
553 | |||
554 | /** |
||
555 | * 获取指定某些键的项 |
||
556 | * |
||
557 | * @param array $array |
||
558 | * @param array|string $keys 数组或逗号字符串 |
||
559 | * |
||
560 | * @return array |
||
561 | * |
||
562 | * @tested |
||
563 | */ |
||
564 | 2 | public static function some($array, $keys = null) |
|
565 | { |
||
566 | 2 | if (null === $keys) { |
|
567 | 1 | return $array; |
|
568 | } |
||
569 | 2 | $keys = Strings::toArray($keys); |
|
570 | 2 | return array_intersect_key($array, array_flip($keys)); |
|
571 | } |
||
572 | |||
573 | /** |
||
574 | * 获取指定除了某些键的项 |
||
575 | * |
||
576 | * @param array $array |
||
577 | * @param array|string $keys |
||
578 | * |
||
579 | * @return array |
||
580 | * |
||
581 | * @tested |
||
582 | */ |
||
583 | 1 | public static function exceptedKeys($array, $keys) |
|
584 | { |
||
585 | 1 | $keys = Strings::toArray($keys); |
|
586 | 1 | return array_diff_key($array, array_flip($keys)); |
|
587 | } |
||
588 | |||
589 | /** |
||
590 | * 检查数组里是否有指定的所有键名或索引 |
||
591 | * |
||
592 | * - array_key_exists:检测一个指定的键 |
||
593 | * - Arrays::keyExistsOne:检测数组里是否存在指定的某些键 |
||
594 | * |
||
595 | * @param array $keys 要检查的键 |
||
596 | * @param array $array |
||
597 | * @param array $diff 引用返回不包含的键 |
||
598 | * |
||
599 | * @return boolean |
||
600 | * |
||
601 | * @tested |
||
602 | */ |
||
603 | 1 | public static function keyExistsAll($keys, $array, &$diff = null) |
|
604 | { |
||
605 | |||
606 | 1 | return I::isEmpty($diff = array_diff($keys, array_keys($array))); |
|
607 | } |
||
608 | |||
609 | /** |
||
610 | * 检查数组里是否有指定的某些键名或索引 |
||
611 | * |
||
612 | * @param array $keys 要检查的键 |
||
613 | * @param array $array |
||
614 | * @param array $find 引用返回包含的键 |
||
615 | * |
||
616 | * @return boolean |
||
617 | * |
||
618 | * @tested |
||
619 | */ |
||
620 | 1 | public static function keyExistsSome($keys, $array, &$find = null) |
|
621 | { |
||
622 | 1 | return !I::isEmpty($find = array_intersect($keys, array_keys($array))); |
|
623 | } |
||
624 | |||
625 | /** |
||
626 | * 检查数组里是否有指定的所有值 |
||
627 | * |
||
628 | * @param array $values 要检查的值 |
||
629 | * @param array $array |
||
630 | * @param array $diff 引用返回不包含的值 |
||
631 | * |
||
632 | * @return boolean |
||
633 | * |
||
634 | * @tested |
||
635 | */ |
||
636 | 1 | public static function valueExistsAll($values, $array, &$diff = null) |
|
637 | { |
||
638 | 1 | return I::isEmpty($diff = array_diff($values, array_values($array))); |
|
639 | } |
||
640 | |||
641 | /** |
||
642 | * 检查数组里是否有指定的某些值 |
||
643 | * |
||
644 | * @param array $values 要检查的值 |
||
645 | * @param array $array |
||
646 | * @param array $find 引用返回包含的值 |
||
647 | * |
||
648 | * @return boolean |
||
649 | * |
||
650 | * @tested |
||
651 | */ |
||
652 | 1 | public static function valueExistsSome($values, $array, &$find = null) |
|
653 | { |
||
654 | 1 | return !I::isEmpty($find = array_intersect($values, array_values($array))); |
|
655 | } |
||
656 | |||
657 | /** |
||
658 | * 参照 PHP 的 array_combine 函数,array_combine 得到的是一行记录的格式,该函数得到多行 |
||
659 | * |
||
660 | * - arrays 里的每个数组会和 keys 使用 self::combine 合并,最终合并成为一个二维数组 |
||
661 | * |
||
662 | * @param array $keys 作为键的字段 |
||
663 | * @param array $arrays |
||
664 | * |
||
665 | * @return array |
||
666 | * |
||
667 | * @tested |
||
668 | */ |
||
669 | 1 | public static function combines($keys, $arrays) |
|
670 | { |
||
671 | 1 | $result = []; |
|
672 | 1 | foreach ($arrays as $k => $array) { |
|
673 | 1 | $result[$k] = self::combine($keys, $array); |
|
674 | } |
||
675 | |||
676 | 1 | return $result; |
|
677 | } |
||
678 | |||
679 | /** |
||
680 | * 把数组里逗号字符串拆分,并且去掉重复的部分 |
||
681 | * |
||
682 | * @param array $array |
||
683 | * @param string $delimiter 可自定义分隔符,默认为英文逗号(,) |
||
684 | * |
||
685 | * @return array |
||
686 | * |
||
687 | * @tested |
||
688 | 2 | */ |
|
689 | public static function toPart($array, $delimiter = ',') |
||
690 | 2 | { |
|
691 | 2 | return array_values( |
|
692 | 2 | array_filter( |
|
693 | 2 | array_keys( |
|
694 | 2 | array_flip( |
|
695 | explode($delimiter, implode($delimiter, $array)) |
||
696 | ) |
||
697 | ) |
||
698 | ) |
||
699 | ); |
||
700 | } |
||
701 | |||
702 | /** |
||
703 | * 矩阵转置 |
||
704 | * |
||
705 | * @param array $array 待转置的矩阵 |
||
706 | * |
||
707 | * @return array |
||
708 | * |
||
709 | * @tested |
||
710 | 1 | */ |
|
711 | public static function transposed($array) |
||
712 | 1 | { |
|
713 | 1 | $data = []; |
|
714 | 1 | foreach ($array as $r => $row) { |
|
715 | 1 | foreach ($row as $c => $col) { |
|
716 | $data[$c][$r] = $col; |
||
717 | } |
||
718 | 1 | } |
|
719 | return $data; |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * 普通二维数组转化成 Excel 单元格二维数组 |
||
724 | * |
||
725 | * @param array $array |
||
726 | * |
||
727 | * @return array |
||
728 | * |
||
729 | * @tested |
||
730 | 1 | */ |
|
731 | public static function toCellArray($array) |
||
732 | 1 | { |
|
733 | 1 | $data = []; |
|
734 | 1 | $rowIndex = 0; |
|
735 | 1 | foreach ($array as $row) { |
|
736 | 1 | $rowIndex++; |
|
737 | 1 | $colIndex = 'A'; |
|
738 | 1 | foreach ($row as $col) { |
|
739 | $data[$rowIndex][$colIndex++] = $col; |
||
740 | } |
||
741 | 1 | } |
|
742 | return $data; |
||
743 | } |
||
744 | |||
745 | /** |
||
746 | * 返回矩阵的列数和行数 |
||
747 | * |
||
748 | * - 返回两个元素的一维数组,第一个元素表示矩阵的列数,第二个元素表示矩阵的行数 |
||
749 | * |
||
750 | * @param array $array |
||
751 | * |
||
752 | * @return array |
||
753 | * |
||
754 | * @tested |
||
755 | 2 | */ |
|
756 | public static function colRowCount($array) |
||
757 | 2 | { |
|
758 | return [self::count(self::first($array)), self::count($array)]; |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * 用给定的值填充数组 |
||
763 | * |
||
764 | * - array_fill:第一参数在为负的时候,生成的数组的第二个元素是从 0 开始的! |
||
765 | * |
||
766 | * @param int $startIndex 返回的数组的第一个索引值 |
||
767 | * @param int $num 插入元素的数量。如果为 0 或者负数,则返回空数组 |
||
768 | * @param mixed $value 用来填充的值 |
||
769 | * |
||
770 | * @return array |
||
771 | * |
||
772 | * @tested |
||
773 | 2 | */ |
|
774 | public static function fill($startIndex, $num, $value) |
||
775 | 2 | { |
|
776 | 1 | if ($num <= 0) { |
|
777 | return []; |
||
778 | 2 | } |
|
779 | 2 | $array = []; |
|
780 | 2 | foreach (self::rangeGenerator($startIndex, $startIndex + $num - 1) as $key) { |
|
781 | $array[$key] = $value; |
||
782 | 2 | } |
|
783 | return $array; |
||
784 | } |
||
785 | |||
786 | /** |
||
787 | * 让 var_export 返回 `[]` 的格式 |
||
788 | * |
||
789 | * @param mixed $expression 变量 |
||
790 | * @param bool $return 默认值 为 true,即返回字符串而不是输出 |
||
791 | * |
||
792 | * @return mixed |
||
793 | * |
||
794 | * @tested |
||
795 | 1 | */ |
|
796 | public static function export($expression, $return = true) |
||
797 | 1 | { |
|
798 | 1 | $export = var_export($expression, true); |
|
799 | 1 | $export = preg_replace("/^([ ]*)(.*)/m", '$1$1$2', $export); |
|
800 | 1 | $array = preg_split("/\r\n|\n|\r/", $export); |
|
801 | 1 | $array = preg_replace(["/\s*array\s\($/", "/\)(,)?$/", "/\s=>\s$/"], [null, ']$1', ' => ['], $array); |
|
802 | 1 | $export = implode(PHP_EOL, array_filter(["["] + $array)); |
|
803 | 1 | if (true === $return) { |
|
804 | return $export; |
||
805 | 1 | } else { |
|
806 | echo $export; |
||
807 | 1 | } |
|
808 | } |
||
809 | |||
810 | /** |
||
811 | * 将 CSV 文本转成数组 |
||
812 | * |
||
813 | * @param string $csvString |
||
814 | * |
||
815 | * @return array |
||
816 | * |
||
817 | * @tested |
||
818 | 1 | */ |
|
819 | public static function fromCsv($csvString) |
||
820 | 1 | { |
|
821 | 1 | $lines = explode(PHP_EOL, $csvString); |
|
822 | 1 | $array = []; |
|
823 | 1 | foreach ($lines as $line) { |
|
824 | $array[] = explode(',', $line); |
||
825 | 1 | } |
|
826 | return $array; |
||
827 | } |
||
828 | |||
829 | /** |
||
830 | * 将查询记录格式的数组转成 csv 文本 |
||
831 | * |
||
832 | * @param array^2 记录 |
||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||
833 | * |
||
834 | * @return string |
||
835 | */ |
||
836 | public static function recordsToCsv($records) |
||
837 | { |
||
838 | if (0 === self::count($records)) { |
||
839 | return ''; |
||
840 | } |
||
841 | $header = array_keys($records[0]); |
||
842 | $arrays = self::values($records); |
||
843 | 1 | return implode(',', $header) . PHP_EOL . implode(PHP_EOL, self::map($arrays, function ($array) { |
|
844 | return implode(',', $array); |
||
845 | 1 | })); |
|
846 | 1 | } |
|
847 | |||
848 | 1 | /** |
|
849 | 1 | * 在数组中搜索给定的值,如果成功则返回首个相应的键名 |
|
850 | 1 | * |
|
851 | 1 | * - 第一参数如果不是回调函数,则此方法等同于 array_search |
|
852 | * - 第一参数如果是回调函数,则找到的条件为:回调值为 true |
||
853 | * - 第三参数如果是 false,则回调值只需要 true 值即可(例如:1) |
||
854 | 1 | * |
|
855 | * @param mixed|callback $search 搜索的值 |
||
856 | * @param array $array 这个数组 |
||
857 | * @param boolean $isStrict 是否检查完全相同的元素 |
||
858 | * |
||
859 | * @return mixed|false |
||
860 | * |
||
861 | * @tested |
||
862 | */ |
||
863 | public static function search($search, $array, $isStrict = false) |
||
864 | { |
||
865 | if (false === is_callable($search)) { |
||
866 | return array_search($search, $array, $isStrict); |
||
867 | } |
||
868 | foreach ($array as $key => $row) { |
||
869 | $result = I::call($search, [$row]); |
||
870 | 1 | if ((true === $isStrict && true === $result) || (false === $isStrict && true == $result)) { |
|
871 | return $key; |
||
872 | 1 | } |
|
873 | 1 | } |
|
874 | return false; |
||
875 | } |
||
876 | |||
877 | /** |
||
878 | * 递增数组的一个值并返回 |
||
879 | * |
||
880 | * - 如果该值不存在,则默认为 0 |
||
881 | * |
||
882 | * @param array $array 引用返回数组 |
||
883 | * @param string $key |
||
884 | * @param integer $step 步长,默认 1 |
||
885 | * |
||
886 | * @return double|integer |
||
887 | * |
||
888 | * @tested |
||
889 | 1 | */ |
|
890 | public static function increment(&$array, $key, $step = 1) |
||
891 | 1 | { |
|
892 | 1 | $array[$key] = I::get($array, $key, 0) + $step; |
|
893 | return $array[$key]; |
||
894 | } |
||
895 | |||
896 | /** |
||
897 | * 递减数组的一个值并返回 |
||
898 | * |
||
899 | * - 如果该值不存在,则默认为 0 |
||
900 | * |
||
901 | * @param array $array 引用返回数组 |
||
902 | * @param string $key |
||
903 | * @param integer $step 步长,默认 1 |
||
904 | * |
||
905 | 3 | * @return double|integer |
|
906 | * |
||
907 | 3 | * @tested |
|
908 | 1 | */ |
|
909 | public static function decrement(&$array, $key, $step = 1) |
||
910 | 3 | { |
|
911 | 1 | $array[$key] = I::get($array, $key, 0) - $step; |
|
912 | return $array[$key]; |
||
913 | 3 | } |
|
914 | 3 | ||
915 | 3 | /** |
|
916 | * in_array 的扩展 |
||
917 | * |
||
918 | * @param mixed $value |
||
919 | * @param array|mixed $array |
||
920 | * @param boolean $isStrict 是否严格匹配,默认 false,即不严格 |
||
921 | * @param boolean $ignoreCase 是否忽略大小写,默认 false,不忽略 |
||
922 | * |
||
923 | * @return boolean |
||
924 | */ |
||
925 | public static function in($value, $array, $isStrict = false, $ignoreCase = false) |
||
926 | { |
||
927 | if (false === is_array($array)) { |
||
928 | return false; |
||
929 | } |
||
930 | if (false === $ignoreCase) { |
||
931 | return in_array($value, $array, $isStrict); |
||
932 | } else { |
||
933 | $value = Json::decode(strtolower(Json::encode($value))); |
||
934 | $array = (array) Json::decode(strtolower(Json::encode($array))); |
||
935 | return in_array($value, $array, $isStrict); |
||
936 | } |
||
937 | } |
||
938 | |||
939 | /** |
||
940 | * 对一个数组执行回调并返回新数组 |
||
941 | * |
||
942 | * @param array|mixed $array |
||
943 | * @param callback|null $valueCallback 对值的回调,null 返回原值。function($value, $key) |
||
944 | * @param callback|null $keyCallback 对键的回调,null 返回原值。function($key, $value) |
||
945 | * |
||
946 | * @return array |
||
947 | */ |
||
948 | public static function map($array, $valueCallback, $keyCallback = null) |
||
949 | { |
||
950 | $return = []; |
||
951 | if (is_array($array)) { |
||
952 | foreach ($array as $key => $value) { |
||
953 | $callValue = null === $valueCallback ? $value : I::call($valueCallback, [$value, $key]); |
||
954 | $callKey = null === $keyCallback ? $key : I::call($keyCallback, [$key, $value]); |
||
955 | $return[$callKey] = $callValue; |
||
956 | } |
||
957 | } |
||
958 | return $return; |
||
959 | } |
||
960 | |||
961 | /** |
||
962 | * 排队取号 |
||
963 | * |
||
964 | * - 含义:给定只包含数字的索引数组,索引数组的值作为号码从小到大排序,如果中间漏掉某个号码,则返回该号码,否则返回最后一位号码,并且引用返回该队列 |
||
965 | * - 号码的值从 0 开始 |
||
966 | * - 例子:[4, 0, 1] 将引用返回数组[4, 0, 1, 2],函数返回 2 |
||
967 | * |
||
968 | * @param array $array |
||
969 | * @param integer $begin 开始的号码,默认为 0 |
||
970 | * |
||
971 | * @return integer |
||
972 | */ |
||
973 | public static function queueNumber(&$array, $begin = 0) |
||
974 | { |
||
975 | $array2 = $array; |
||
976 | sort($array2); |
||
977 | $next = $begin; |
||
978 | foreach ($array2 as $v) { |
||
979 | if ($v > $next) { |
||
980 | break; |
||
981 | } elseif ($v < $next) { |
||
982 | if ($v + 1 > $next) { |
||
983 | $next = $v + 1; |
||
984 | } |
||
985 | } else { |
||
986 | $next++; |
||
987 | } |
||
988 | } |
||
989 | array_push($array, $next); |
||
990 | return $next; |
||
991 | } |
||
992 | |||
993 | /** |
||
994 | * 将一维数组的每项用分隔符拆分,得到两部分分别设置为新数组的键和值 |
||
995 | * |
||
996 | * @param string $delimiter 分隔符 |
||
997 | * @param array $array |
||
998 | * |
||
999 | * @return array |
||
1000 | */ |
||
1001 | public static function explode($delimiter, $array) |
||
1002 | { |
||
1003 | $return = []; |
||
1004 | foreach ($array as $row) { |
||
1005 | list($k, $v) = Arrays::lists(explode($delimiter, $row), 2); |
||
1006 | $return[$k] = $v; |
||
1007 | } |
||
1008 | return $return; |
||
1009 | } |
||
1010 | } |
||
1011 |