Skip to content

Commit

Permalink
Added new yii\helpers\ArrayHelper::flatten() method
Browse files Browse the repository at this point in the history
  • Loading branch information
xcopy committed Jan 5, 2025
1 parent 3fc7e71 commit 698eb10
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 49 deletions.
57 changes: 57 additions & 0 deletions framework/helpers/BaseArrayHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -1043,4 +1043,61 @@ public static function recursiveSort(array &$array, $sorter = null)

return $array;
}

/**
* Flattens a multidimensional array into a one-dimensional array.
*
* This method recursively traverses the input array and concatenates the keys
* in a dot format to form a new key in the resulting array.
*
* Example:
*
* ```php
* $array = [
* 'A' => [1, 2],
* 'B' => [
* 'C' => 1,
* 'D' => 2,
* ],
* 'E' => 1,
* ];
* $result = \yii\helpers\ArrayHelper::flatten($array);
* // $result will be:
* // [
* // 'A.0' => 1
* // 'A.1' => 2
* // 'B.C' => 1
* // 'B.D' => 2
* // 'E' => 1
* // ]
* ```
*
* @param array $array the input array to be flattened in terms of name-value pairs.
* @param string $separator the separator to use between keys. Defaults to '.'.
*
* @return array the flattened array.
* @throws InvalidArgumentException if `$array` is neither traversable nor an array.
*/
public static function flatten($array, $separator = '.'): array
{
if (!static::isTraversable($array)) {
throw new InvalidArgumentException('Argument $array must be an array or implement Traversable');

Check warning on line 1084 in framework/helpers/BaseArrayHelper.php

View check run for this annotation

Codecov / codecov/patch

framework/helpers/BaseArrayHelper.php#L1084

Added line #L1084 was not covered by tests
}

$result = [];

foreach ($array as $key => $value) {
$newKey = $key;
if (is_array($value)) {
$flattenedArray = self::flatten($value, $separator);
foreach ($flattenedArray as $subKey => $subValue) {
$result[$newKey . $separator . $subKey] = $subValue;
}
} else {
$result[$newKey] = $value;
}
}

return $result;
}
}
50 changes: 1 addition & 49 deletions framework/log/Target.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,54 +167,6 @@ public function collect($messages, $final)
}
}

/**
* Flattens a multidimensional array into a one-dimensional array.
*
* This method recursively traverses the input array and concatenates the keys
* to form a new key in the resulting array.
*
* Example:
*
* ```php
* $array = [
* 'A' => [1, 2],
* 'B' => [
* 'C' => 1,
* 'D' => 2,
* ],
* 'E' => 1,
* ];
* $result = \yii\log\Target::flatten($array);
* // result will be:
* // [
* // 'A.0' => 1
* // 'A.1' => 2
* // 'B.C' => 1
* // 'B.D' => 2
* // 'E' => 1
* // ]
* ```
*
* @param array $array the input array to be flattened.
* @param string $prefix the prefix to be added to each key in the resulting array.
*
* @return array the flattened array.
*/
private static function flatten($array, $prefix = ''): array
{
$result = [];

foreach ($array as $key => $value) {
if (is_array($value)) {
$result = array_merge($result, self::flatten($value, $prefix . $key . '.'));
} else {
$result[$prefix . $key] = $value;
}
}

return $result;
}

/**
* Generates the context information to be logged.
* The default implementation will dump user information, system variables, etc.
Expand All @@ -223,7 +175,7 @@ private static function flatten($array, $prefix = ''): array
protected function getContextMessage()
{
$context = ArrayHelper::filter($GLOBALS, $this->logVars);
$items = self::flatten($context);
$items = ArrayHelper::flatten($context);
foreach ($this->maskVars as $var) {
foreach ($items as $key => $value) {
if (StringHelper::matchWildcard($var, $key, ['caseSensitive' => false])) {
Expand Down
119 changes: 119 additions & 0 deletions tests/framework/helpers/ArrayHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,125 @@ public function dataProviderRecursiveSort()
],
];
}

public function testFlatten()
{
// Test with deeply nested arrays
$array = [
'a' => [
'b' => [
'c' => [
'd' => 1,
'e' => 2,
],
'f' => 3,
],
'g' => 4,
],
'h' => 5,
];
$expected = [
'a.b.c.d' => 1,
'a.b.c.e' => 2,
'a.b.f' => 3,
'a.g' => 4,
'h' => 5,
];
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Test with arrays containing different data types
$array = [
'a' => [
'b' => [
'c' => 'string',
'd' => 123,
'e' => true,
'f' => null,
],
'g' => [1, 2, 3],
],
];
$expected = [
'a.b.c' => 'string',
'a.b.d' => 123,
'a.b.e' => true,
'a.b.f' => null,
'a.g.0' => 1,
'a.g.1' => 2,
'a.g.2' => 3,
];
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Test with arrays containing special characters in keys
$array = [
'a.b' => [
'c.d' => [
'e.f' => 1,
],
],
'g.h' => 2,
];
$expected = [
'a.b.c.d.e.f' => 1,
'g.h' => 2,
];
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Test with custom separator
$array = [
'a' => [
'b' => [
'c' => [
'd' => 1,
'e' => 2,
],
'f' => 3,
],
'g' => 4,
],
'h' => 5,
];
$result = ArrayHelper::flatten($array, '_');
$expected = [
'a_b_c_d' => 1,
'a_b_c_e' => 2,
'a_b_f' => 3,
'a_g' => 4,
'h' => 5,
];

$this->assertEquals($expected, $result);
}

public function testFlattenEdgeCases()
{
// Empty array
$array = [];
$expected = [];
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Non-array value
$array = 'string';
$expected = ['string'];
$this->expectException('yii\base\InvalidArgumentException');
$this->expectExceptionMessage('Argument $array must be an array or implement Traversable');
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Special characters in keys
$array = ['a.b' => ['c.d' => 1]];
$expected = ['a.b.c.d' => 1];
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Mixed data types
$array = ['a' => ['b' => 'string', 'c' => 123, 'd' => true, 'e' => null]];
$expected = ['a.b' => 'string', 'a.c' => 123, 'a.d' => true, 'a.e' => null];
$this->assertEquals($expected, ArrayHelper::flatten($array));

// Key collisions
$array = ['a' => ['b' => 1], 'a.b' => 2];
$expected = ['a.b' => 2];
$this->assertEquals($expected, ArrayHelper::flatten($array));
}
}

class Post1
Expand Down

0 comments on commit 698eb10

Please sign in to comment.