Passed
Push — master ( f358a5...b5f949 )
by Pauli
03:17
created

ArrayUtil::groupBy()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
nc 2
nop 2
dl 0
loc 6
rs 10
c 1
b 0
f 0
1
<?php declare(strict_types=1);
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Pauli Järvinen <[email protected]>
10
 * @copyright Pauli Järvinen 2025
11
 */
12
13
namespace OCA\Music\Utility;
14
15
/**
16
 * Static utility functions to work with arrays
17
 */
18
class ArrayUtil {
19
20
	/**
21
	 * Extract ID of each array element by calling getId and return
22
	 * the IDs as an array
23
	 */
24
	public static function extractIds(array $arr) : array {
25
		return \array_map(fn($i) => $i->getId(), $arr);
26
	}
27
28
	/**
29
	 * Extract User ID of each array element by calling getUserId and return
30
	 * the IDs as an array
31
	 */
32
	public static function extractUserIds(array $arr) : array {
33
		return \array_map(fn($i) => $i->getUserId(), $arr);
34
	}
35
36
	/**
37
	 * Create a look-up table from given array of items which have a `getId` function.
38
	 * @return array where keys are the values returned by `getId` of each item
39
	 */
40
	public static function createIdLookupTable(array $array) : array {
41
		$lut = [];
42
		foreach ($array as $item) {
43
			$lut[$item->getId()] = $item;
44
		}
45
		return $lut;
46
	}
47
48
	/**
49
	 * Create a look-up table from given array so that keys of the table are obtained by calling
50
	 * the given method on each array entry and the values are arrays of entries having the same
51
	 * value returned by that method.
52
	 * @param string $getKeyMethod Name of a method found on $array entries which returns a string or an int
53
	 * @return array [int|string => array]
54
	 */
55
	public static function groupBy(array $array, string $getKeyMethod) : array {
56
		$lut = [];
57
		foreach ($array as $item) {
58
			$lut[$item->$getKeyMethod()][] = $item;
59
		}
60
		return $lut;
61
	}
62
63
	/**
64
	 * Get difference of two arrays, i.e. elements belonging to $b but not $a.
65
	 * This function is faster than the built-in array_diff for large arrays but
66
	 * at the expense of higher RAM usage and can be used only for arrays of
67
	 * integers or strings.
68
	 * From https://stackoverflow.com/a/8827033
69
	 */
70
	public static function diff(array $b, array $a) : array {
71
		$at = \array_flip($a);
72
		$d = [];
73
		foreach ($b as $i) {
74
			if (!isset($at[$i])) {
75
				$d[] = $i;
76
			}
77
		}
78
		return $d;
79
	}
80
81
	/**
82
	 * Get multiple items from @a $array, as indicated by a second array @a $keys.
83
	 * If @a $preserveKeys is given as true, the result will have the original keys, otherwise
84
	 * the result is re-indexed with keys 0, 1, 2, ...
85
	 */
86
	public static function multiGet(array $array, array $keys, bool $preserveKeys=false) : array {
87
		$result = [];
88
		foreach ($keys as $key) {
89
			if ($preserveKeys) {
90
				$result[$key] = $array[$key];
91
			} else {
92
				$result[] = $array[$key];
93
			}
94
		}
95
		return $result;
96
	}
97
98
	/**
99
	 * Get multiple columns from the multidimensional @a $array. This is similar to the built-in
100
	 * function \array_column except that this can return multiple columns and not just one.
101
	 * @param int|string|null $indexColumn
102
	 */
103
	public static function columns(array $array, array $columns, $indexColumn=null) : array {
104
		if ($indexColumn !== null) {
105
			$array = \array_column($array, null, $indexColumn);
106
		}
107
108
		return \array_map(fn($row) => self::multiGet($row, $columns, true), $array);
109
	}
110
111
	/**
112
	 * Like the built-in function \array_filter but this one works recursively on nested arrays.
113
	 * Another difference is that this function always requires an explicit callback condition.
114
	 * Both inner nodes and leafs nodes are passed to the $condition.
115
	 */
116
	public static function filterRecursive(array $array, callable $condition) : array {
117
		$result = [];
118
119
		foreach ($array as $key => $value) {
120
			if ($condition($value)) {
121
				if (\is_array($value)) {
122
					$result[$key] = self::filterRecursive($value, $condition);
123
				} else {
124
					$result[$key] = $value;
125
				}
126
			}
127
		}
128
129
		return $result;
130
	}
131
132
	/**
133
	 * Inverse operation of self::filterRecursive, keeping only those items where
134
	 * the $condition evaluates to *false*.
135
	 */
136
	public static function rejectRecursive(array $array, callable $condition) : array {
137
		$invCond = fn($item) => !$condition($item);
138
		return self::filterRecursive($array, $invCond);
139
	}
140
141
	/**
142
	 * Convert the given array $arr so that keys of the potentially multi-dimensional array
143
	 * are converted using the mapping given in $dictionary. Keys not found from $dictionary
144
	 * are not altered.
145
	 */
146
	public static function convertKeys(array $arr, array $dictionary) : array {
147
		$newArr = [];
148
149
		foreach ($arr as $k => $v) {
150
			$key = $dictionary[$k] ?? $k;
151
			$newArr[$key] = \is_array($v) ? self::convertKeys($v, $dictionary) : $v;
152
		}
153
154
		return $newArr;
155
	}
156
157
	/**
158
	 * Walk through the given, potentially multi-dimensional, array and cast all leaf nodes
159
	 * to integer type. The array is modified in-place. Optionally, apply the conversion only
160
	 * on the leaf nodes matching the given predicate.
161
	 */
162
	public static function intCastValues(array &$arr, ?callable $predicate=null) : void {
163
		\array_walk_recursive($arr, function(&$value) use($predicate) {
164
			if ($predicate === null || $predicate($value)) {
165
				$value = (int)$value;
166
			}
167
		});
168
	}
169
170
	/**
171
	 * Given a two-dimensional array, sort the outer dimension according to values in the
172
	 * specified column of the inner dimension.
173
	 */
174
	public static function sortByColumn(array &$arr, string $column) : void {
175
		\usort($arr, fn($a, $b) => StringUtil::caselessCompare($a[$column], $b[$column]));
176
	}
177
178
}