Skip to content

Commit 8466678

Browse files
committed
Unpack method refactor and isAssoc documentation improvement
1 parent 5165980 commit 8466678

File tree

3 files changed

+89
-14
lines changed

3 files changed

+89
-14
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Here, you can quickly get started by becoming familiar with each and every metho
4545
* [hasKeys](#haskeysarray-array-mixed-keys-bool-strict--false-bool)
4646
* [getNestedElement](#getnestedelementarrayarrayaccess-array-mixed-keys-mixed-default--null-mixed)
4747
* [setNestedElement](#setnestedelementarray-array-mixed-keys-mixed-value-array)
48-
* [unpack](#unpackarray-array-array-keys---array)
48+
* [unpack](#unpackarray-array-int-mode--arrunpack_all-array)
4949
### Validation
5050
* [check](#checkarray-array-mixedcallable-condition-bool-strict--false-bool)
5151
* [isEmpty](#isemptymixed-array-bool)
@@ -159,7 +159,7 @@ Arr::setNestedElement($array, 'foo.[].foo', 'bar') ->
159159
Arr::setNestedElement([], '[].[].[]', 'test') -> [ [ [ 'test' ] ] ]
160160
```
161161

162-
### `unpack(array $array, array $keys = []): array`
162+
### `unpack(array $array, int $mode = Arr::UNPACK_ALL): array`
163163
Converts multidimensional array to map of keys concatenated by dot and corresponding values
164164

165165
```php

src/Arr.php

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,22 @@ class Arr
3232
const MAP_ARRAY_VALUE_KEYS_LIST = 2;
3333
const MAP_ARRAY_KEYS_ARRAY_VALUE = 4;
3434

35+
const UNPACK_ALL = 1;
36+
/**
37+
* Preserve arrays with highest nesting level (if they are not assoc) as element values instead of unpacking them
38+
*/
39+
const UNPACK_PRESERVE_LIST_ARRAY = 2;
40+
/**
41+
* Preserve arrays with highest nesting level (if they are assoc) as element values instead of unpacking them
42+
*/
43+
const UNPACK_PRESERVE_ASSOC_ARRAY = 4;
44+
/**
45+
* Preserve all arrays with highest nesting level as element values instead of unpacking them
46+
*/
47+
const UNPACK_PRESERVE_ARRAY = 8;
3548

3649
private const AUTO_INDEX_KEY = '[]';
50+
private const KEY_SEPARATOR = '.';
3751

3852
/*--------------------------------------------------------------------------------------*\
3953
| Common |
@@ -58,7 +72,7 @@ class Arr
5872
public static function getKeysArray($keys): array
5973
{
6074
if (is_string($keys)) {
61-
return empty($keys) ? [] : explode('.', $keys);
75+
return empty($keys) ? [] : explode(self::KEY_SEPARATOR, $keys);
6276
}
6377
return is_null($keys) ? [] : array_filter(array_values(self::forceArray($keys)), function ($value) {
6478
return $value !== null && $value !== '' && (is_string($value) || is_int($value));
@@ -153,21 +167,34 @@ public static function setNestedElement(array $array, $keys, $value): array
153167
* Converts multidimensional array to map of keys concatenated by dot and corresponding values
154168
*
155169
* @param array $array
156-
* @param array $keys Internal keys storage used for recursive calls
170+
* @param int $mode Modify behaviour of unpack (see description of Arr::UNPACK_ constants)
157171
* @return array
158172
*/
159-
public static function unpack(array $array, array $keys = []): array
173+
public static function unpack(array $array, int $mode = self::UNPACK_ALL): array
174+
{
175+
return self::_unpack($array, $mode);
176+
}
177+
178+
private static function _unpack(array $array, int $mode = self::UNPACK_ALL, array $keys = []): array
160179
{
161180
$result = [];
162181

163182
foreach ($array as $key => $value) {
164183

165-
if (is_array($value)) {
184+
if (is_array($value) && !(
185+
// Check if value IS NOT a subject for preserve mode
186+
!self::isNested($value) && // Preserve mode only work for highest depth elements
187+
(
188+
($mode === self::UNPACK_PRESERVE_LIST_ARRAY && !self::isAssoc($value, true)) ||
189+
($mode === self::UNPACK_PRESERVE_ASSOC_ARRAY && self::isAssoc($value, true)) ||
190+
$mode === self::UNPACK_PRESERVE_ARRAY
191+
)
192+
)) {
166193
$keys[] = $key;
167-
$result += self::unpack($value, $keys);
194+
$result += self::_unpack($value, $mode, $keys);
168195
array_pop($keys);
169196
} else {
170-
$result[implode('.', array_merge($keys, [$key]))] = $value;
197+
$result[implode(self::KEY_SEPARATOR, array_merge($keys, [$key]))] = $value;
171198
}
172199
}
173200

@@ -251,7 +278,9 @@ public static function isEmpty($array): bool
251278
*
252279
* @param array $array
253280
* @param bool $strict
254-
* If false then this function will match any array that doesn't contain integer keys
281+
* <p>If <i>false</i> then this function will match any array that doesn't contain integer keys.</p>
282+
* <p>If <i>true</i> then this function match only arrays with sequence of integers starting from zero (range from 0 to elements_number - 1) as keys.</p>
283+
*
255284
* @return boolean
256285
*/
257286
public static function isAssoc(array $array, bool $strict = false): bool
@@ -772,7 +801,7 @@ public static function createMulti(array $keys, ?array $values = null): array
772801
* Make variable an array (according to flag settings)
773802
*
774803
* @param mixed $var
775-
* @param int $flag Set flag(s) to preserve specific values from being converted to array
804+
* @param int $flag Set flag(s) to preserve specific values from being converted to array (see Arr::FORCE_ARRAY_ constants)
776805
* @return array
777806
*/
778807
public static function forceArray($var, int $flag = self::FORCE_ARRAY_ALL)

test/ArrTest.php

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,24 +120,70 @@ public function testUnpack()
120120
'key3' => ['test', 'test2'],
121121
'key4' => 'test3'
122122
],
123-
1
123+
1 => ['a' => 'b', 'c'],
124124
],
125-
2,
125+
2 => [3 => 4, 5 => 6],
126126
4 => 56
127127
];
128128
$array2 = [1, 2, 3, 4, 5];
129129

130+
131+
// Test default mode
130132
$this->assertSame([
131133
'key1.key2.key3.0' => 'test',
132134
'key1.key2.key3.1' => 'test2',
133135
'key1.key2.key4' => 'test3',
134-
'key1.0' => 1,
135-
0 => 2,
136+
'key1.1.a' => 'b',
137+
'key1.1.0' => 'c',
138+
'2.3' => 4,
139+
'2.5' => 6,
136140
4 => 56,
137141

138142
], Arr::unpack($array));
139143

140144
$this->assertSame($array2, Arr::unpack($array2));
145+
146+
147+
// Test UNPACK_PRESERVE_LIST_ARRAY mode
148+
$this->assertSame([
149+
'key1.key2.key3' => ['test', 'test2'],
150+
'key1.key2.key4' => 'test3',
151+
'key1.1.a' => 'b',
152+
'key1.1.0' => 'c',
153+
'2.3' => 4,
154+
'2.5' => 6,
155+
4 => 56,
156+
157+
], Arr::unpack($array, Arr::UNPACK_PRESERVE_LIST_ARRAY));
158+
159+
$this->assertSame($array2, Arr::unpack($array2, Arr::UNPACK_PRESERVE_LIST_ARRAY));
160+
161+
162+
// Test UNPACK_PRESERVE_ASSOC_ARRAY mode
163+
$this->assertSame([
164+
'key1.key2.key3.0' => 'test',
165+
'key1.key2.key3.1' => 'test2',
166+
'key1.key2.key4' => 'test3',
167+
'key1.1' => ['a' => 'b', 0 => 'c'],
168+
2 => [3 => 4, 5 => 6],
169+
4 => 56,
170+
171+
], Arr::unpack($array, Arr::UNPACK_PRESERVE_ASSOC_ARRAY));
172+
173+
$this->assertSame($array2, Arr::unpack($array2, Arr::UNPACK_PRESERVE_ASSOC_ARRAY));
174+
175+
176+
// Test UNPACK_PRESERVE_ARRAY mode
177+
$this->assertSame([
178+
'key1.key2.key3' => ['test', 'test2'],
179+
'key1.key2.key4' => 'test3',
180+
'key1.1' => ['a' => 'b', 0 => 'c'],
181+
2 => [3 => 4, 5 => 6],
182+
4 => 56,
183+
184+
], Arr::unpack($array, Arr::UNPACK_PRESERVE_ARRAY));
185+
186+
$this->assertSame($array2, Arr::unpack($array2, Arr::UNPACK_PRESERVE_ARRAY));
141187
}
142188

143189
/********************************* Validation *********************************/

0 commit comments

Comments
 (0)