Skip to content
31 changes: 25 additions & 6 deletions src/Support/Arr.php
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
<?php namespace Winter\Storm\Support;

use Illuminate\Support\Arr as ArrHelper;
use InvalidArgumentException;

/**
* Array helper
*
* @author Alexey Bobkov, Samuel Georges
* @author Winter CMS
*/
class Arr extends ArrHelper
{
/**
* Build a new array using a callback.
*
* @param array $array
* @param callable $callback
* @return array
*/
public static function build(array $array, callable $callback)
public static function build(array $array, callable $callback): array
{
$results = [];

Expand All @@ -28,4 +25,26 @@ public static function build(array $array, callable $callback)

return $results;
}

/**
* Moves the key to the index within the array, negative index will work backwards from the end of the array
* @throws InvalidArgumentException if the key does not exist in the array
*/
public static function moveKeyToIndex(array $array, string|int $targetKey, int $index): array
{
if (!array_key_exists($targetKey, $array)) {
throw new InvalidArgumentException(sprintf('Key "%s" does not exist in the array', $targetKey));
}

$keys = array_diff(array_keys($array), [$targetKey]);

array_splice($keys, $index, 0, [$targetKey]);

$sorted = [];
foreach ($keys as $key) {
$sorted[$key] = $array[$key];
}

return $sorted;
}
}
33 changes: 31 additions & 2 deletions src/Support/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

/**
* String helper
*
* @author Alexey Bobkov, Samuel Georges
*/
class Str extends StrHelper
{
Expand Down Expand Up @@ -131,4 +129,35 @@ public static function ordinal($number)
return $number.'th';
}
}

/**
* Ensures that the provide string will be unique within the provided array,
* adjusts it with the separator & step as necessary if not
*
* Examples:
* winter, [winter, winter_1, winter_2] -> winter_3
* winter, [winter_1, winter_3] -> winter
*/
public static function unique(string $str, array $items, string $separator = '_', int $step = 1): string
{
$indexes = [];

if (!in_array($str, $items)) {
return $str;
} else {
$indexes[] = 0;
}

foreach ($items as $item) {
if (!preg_match('/(.*?)' . $str . $separator . '(\d*$)/', $item, $matches)) {
continue;
}

$indexes[] = (int) $matches[2];
}

return empty($indexes)
? $str
: $str . $separator . (max($indexes) + $step);
}
}
53 changes: 53 additions & 0 deletions tests/Support/ArrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,59 @@

class ArrTest extends TestCase
{
public function testMoveKeyToIndex()
{
$array = [
'one' => 'a',
'two' => 'b',
'three' => 'c',
];

// 0 based index means that index 1 is the second element
$this->assertSame([
'one' => 'a',
'three' => 'c',
'two' => 'b',
], Arr::moveKeyToIndex($array, 'three', 1));

// 0 index inserts at start
$this->assertSame([
'two' => 'b',
'one' => 'a',
'three' => 'c',
], Arr::moveKeyToIndex($array, 'two', 0));

// Index out of range inserts at end
$this->assertSame([
'one' => 'a',
'three' => 'c',
'two' => 'b',
], Arr::moveKeyToIndex($array, 'two', 10));

// Negative index works backwards
$this->assertSame([
'two' => 'b',
'one' => 'a',
'three' => 'c',
], Arr::moveKeyToIndex($array, 'two', -2));

// Negative index beyond bounds inserting as first element
$this->assertSame([
'two' => 'b',
'one' => 'a',
'three' => 'c',
], Arr::moveKeyToIndex($array, 'two', -10));

// Elements with null values are correctly able to be sorted
$nullValueArray = $array;
$nullValueArray['two'] = null;
$this->assertSame([
'one' => 'a',
'three' => 'c',
'two' => null,
], Arr::moveKeyToIndex($nullValueArray, 'two', 2));
}

public function testArrClass()
{
$array = [
Expand Down
17 changes: 17 additions & 0 deletions tests/Support/StrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,21 @@ public function testJoin()
$this->assertSame('bob or joe', Str::join(['bob', 'joe'], ', ', ', or ', ' or '));
$this->assertSame('bob; joe; or sally', Str::join(['bob', 'joe', 'sally'], '; ', '; or '));
}

public function testUnique()
{
// Original returned unmodified when already unique
$this->assertSame('winter_cms', Str::unique('winter_cms', []));
$this->assertSame('winter_cms', Str::unique('winter_cms', ['winter_cms_1', 'winter_cms_2']));

// // String modified to be the default step higher than the highest index identified
$this->assertSame('winter_cms_1', Str::unique('winter_cms', ['winter_cms']));
$this->assertSame('winter_cms_4', Str::unique('winter_cms', ['winter_cms', 'winter_cms_1', 'test_5', 'winter_cms_3']));

// String modified to be the default step higher than the highest index identified with reversed order of items
$this->assertSame('winter_cms_98', Str::unique('winter_cms', ['winter_cms', 'winter_cms_97', 'test_5', 'winter_cms_3']));

// String modified to be the provided step higher than the highest index identified with the provided separator
$this->assertSame('winter_cms 5', Str::unique('winter_cms', ['winter_cms', 'winter_cms 1', 'test_5', 'winter_cms 3'], ' ', 2));
}
}