Skip to content

Commit ed118d4

Browse files
committed
Continue utility class cleanup
- Pass value AND key to `FluentIteratorInterface::forEach()` callbacks - Return `null` instead of `false` when `FluentIteratorInterface::nextWithValue()` finds no matching value - Add `Arr::toMap()`, deprecating `Convert::listToMap()` - Add `Get::array()`, deprecating `Convert::iterableToArray()` Remove: - `Convert::iterableToIterator()` - `Convert::iterableToItem()` - `Convert::valueAtKey()` - `Convert::scalarToString()` - `FluentIteratorInterface::forEachWhile()` and `FluentIteratorTrait::forEachWhile()`
1 parent a00d70e commit ed118d4

File tree

12 files changed

+186
-242
lines changed

12 files changed

+186
-242
lines changed

lk-util/Command/Http/SendHttpRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Lkrms\Sync\Concept\HttpSyncProvider;
1111
use Lkrms\Utility\Arr;
1212
use Lkrms\Utility\Convert;
13+
use Lkrms\Utility\Get;
1314

1415
/**
1516
* Sends HTTP requests to HTTP sync providers
@@ -147,7 +148,7 @@ protected function run(string ...$args)
147148

148149
if ($this->Paginate) {
149150
/** @var iterable<array-key,mixed> $result */
150-
$array = Convert::iterableToArray($result);
151+
$array = Get::array($result);
151152
$result = $array;
152153
}
153154

src/Iterator/Concern/FluentIteratorTrait.php

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Lkrms\Iterator\Concern;
44

55
use Lkrms\Iterator\Contract\FluentIteratorInterface;
6+
use ArrayAccess;
67

78
/**
89
* Implements FluentIteratorInterface
@@ -32,54 +33,42 @@ public function toArray(bool $preserveKeys = true): array
3233
}
3334

3435
/**
35-
* @param callable(TValue): mixed $callback
36+
* @param callable(TValue, TKey): mixed $callback
3637
* @return $this
3738
*/
3839
public function forEach(callable $callback)
3940
{
40-
foreach ($this as $current) {
41-
$callback($current);
42-
}
43-
return $this;
44-
}
45-
46-
/**
47-
* @param callable(TValue): (true|mixed) $callback
48-
* @return $this
49-
*/
50-
public function forEachWhile(callable $callback, ?bool &$result = null)
51-
{
52-
foreach ($this as $current) {
53-
if ($callback($current) !== true) {
54-
$result = false;
55-
return $this;
56-
}
41+
foreach ($this as $key => $value) {
42+
$callback($value, $key);
5743
}
58-
$result = true;
5944
return $this;
6045
}
6146

6247
/**
6348
* @param array-key $key
6449
* @param mixed $value
65-
* @return TValue|false
50+
* @return TValue|null
6651
*/
6752
public function nextWithValue($key, $value, bool $strict = false)
6853
{
6954
foreach ($this as $current) {
70-
if (is_array($current) || $current instanceof \ArrayAccess) {
55+
// Move forward-only iterators to the next element
56+
if (isset($found)) {
57+
break;
58+
}
59+
if (is_array($current) || $current instanceof ArrayAccess) {
7160
$_value = $current[$key];
7261
} else {
7362
$_value = $current->$key;
7463
}
7564
if ($strict) {
7665
if ($_value === $value) {
77-
return $current;
66+
$found = $current;
7867
}
7968
} elseif ($_value == $value) {
80-
return $current;
69+
$found = $current;
8170
}
8271
}
83-
return false;
72+
return $found ?? null;
8473
}
8574
}

src/Iterator/Contract/FluentIteratorInterface.php

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,27 @@
1717
interface FluentIteratorInterface extends Arrayable, Traversable
1818
{
1919
/**
20-
* Copy the elements of the iterator to an array
20+
* Copy the iterator's elements to an array
2121
*
2222
* @return ($preserveKeys is true ? array<TKey,TValue> : list<TValue>)
2323
*/
2424
public function toArray(bool $preserveKeys = true): array;
2525

2626
/**
27-
* Apply a callback to the elements of the iterator
27+
* Apply a callback to the iterator's elements
2828
*
29-
* @param callable(TValue): mixed $callback
29+
* @param callable(TValue, TKey): mixed $callback
3030
* @return $this
3131
*/
3232
public function forEach(callable $callback);
3333

3434
/**
35-
* Apply a callback to the elements of the iterator until cancelled by the
36-
* callback
37-
*
38-
* @param callable(TValue): (true|mixed) $callback Return `true` to continue
39-
* iterating over the iterator.
40-
* @param bool|null $result If `$result` is provided, `false` is assigned if
41-
* iteration is cancelled by the callback, otherwise `true` is assigned.
42-
* @return $this
43-
*/
44-
public function forEachWhile(callable $callback, ?bool &$result = null);
45-
46-
/**
47-
* Get the next element with a key or property that matches a value
48-
*
49-
* If the current element has `$value` at `$key`, it is returned after
50-
* moving the iterator forward.
35+
* Get the value of the iterator's first element with a key or property
36+
* equal to a given value
5137
*
5238
* @param array-key $key
5339
* @param mixed $value
54-
* @return TValue|false `false` if no matching element is found.
40+
* @return TValue|null `null` if no matching element is found.
5541
*/
5642
public function nextWithValue($key, $value, bool $strict = false);
5743
}

src/Iterator/RecursiveFilesystemIterator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ public function nextWithValue($key, $value, bool $strict = false)
479479
}
480480
}
481481

482-
return false;
482+
return null;
483483
}
484484

485485
private function getPath(string $path, FilesystemIterator $iterator): string

src/Sync/Concept/SyncDefinition.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ function (ISyncContext $ctx, $id, ...$args) use ($closure) {
344344
$this
345345
->getFluentIterator($closure($ctx, ...$args))
346346
->nextWithValue('Id', $id);
347-
if ($entity === false) {
347+
if ($entity === null) {
348348
throw new SyncEntityNotFoundException($this->Provider, $this->Entity, $id);
349349
}
350350
return $entity;

src/Sync/Support/SyncEntityResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ public function getByName(string $name, ?float &$uncertainty = null): ?ISyncEnti
4545
->nextWithValue($this->NameProperty, $name);
4646
break;
4747
} catch (SyncFilterPolicyViolationException $ex) {
48-
$match = false;
48+
$match = null;
4949
continue;
5050
}
5151
}
5252

53-
if ($match === false) {
53+
if ($match === null) {
5454
$uncertainty = null;
5555
return null;
5656
}

src/Utility/Arr.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Lkrms\Concept\Utility;
66
use Lkrms\Contract\Jsonable;
77
use Lkrms\Utility\Catalog\SortTypeFlag;
8+
use ArrayAccess;
89
use OutOfRangeException;
910
use Stringable;
1011

@@ -773,6 +774,27 @@ public static function toIndex(array $array, $value = true): array
773774
);
774775
}
775776

777+
/**
778+
* Index an array by an identifier unique to each value
779+
*
780+
* @template TValue of ArrayAccess|array|object
781+
*
782+
* @param array<TValue> $array
783+
* @param array-key $key
784+
* @return array<TValue>
785+
*/
786+
public static function toMap(array $array, $key): array
787+
{
788+
foreach ($array as $item) {
789+
$map[
790+
is_array($item) || $item instanceof ArrayAccess
791+
? $item[$key]
792+
: $item->$key
793+
] = $item;
794+
}
795+
return $map ?? [];
796+
}
797+
776798
/**
777799
* Apply a callback to a value for each of the elements of an array
778800
*

src/Utility/Convert.php

Lines changed: 7 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,10 @@
77
use Lkrms\Http\Uri;
88
use Lkrms\Support\Catalog\RegularExpression as Regex;
99
use Lkrms\Support\DateFormatter;
10-
use ArrayAccess;
11-
use ArrayIterator;
12-
use Closure;
1310
use DateInterval;
1411
use DateTimeImmutable;
1512
use DateTimeInterface;
1613
use DateTimeZone;
17-
use Iterator;
18-
use IteratorIterator;
1914
use LogicException;
2015
use Stringable;
2116

@@ -275,38 +270,16 @@ public static function coalesce(...$values)
275270
}
276271

277272
/**
278-
* If an iterable isn't already an array, make it one
279-
*
280273
* @template TKey of array-key
281274
* @template TValue
282-
*
283275
* @param iterable<TKey,TValue> $iterable
284276
* @return array<TKey,TValue>
277+
* @deprecated Use {@see Get::array()} instead
278+
* @codeCoverageIgnore
285279
*/
286280
public static function iterableToArray(iterable $iterable, bool $preserveKeys = false): array
287281
{
288-
return is_array($iterable) ? $iterable : iterator_to_array($iterable, $preserveKeys);
289-
}
290-
291-
/**
292-
* If an iterable isn't already an Iterator, enclose it in one
293-
*
294-
* @template TKey of array-key
295-
* @template TValue
296-
*
297-
* @param iterable<TKey,TValue> $iterable
298-
* @return Iterator<TKey,TValue>
299-
*/
300-
public static function iterableToIterator(iterable $iterable): Iterator
301-
{
302-
if ($iterable instanceof Iterator) {
303-
return $iterable;
304-
}
305-
if (is_array($iterable)) {
306-
return new ArrayIterator($iterable);
307-
}
308-
309-
return new IteratorIterator($iterable);
282+
return Get::array($iterable, $preserveKeys);
310283
}
311284

312285
/**
@@ -385,123 +358,16 @@ public static function classToNamespace(string $class): string
385358
}
386359

387360
/**
388-
* Create a map from a list
389-
*
390-
* For example, to map from each array's `id` to the array itself:
391-
*
392-
* ```php
393-
* $list = [
394-
* ['id' => 32, 'name' => 'Greta'],
395-
* ['id' => 71, 'name' => 'Terry'],
396-
* ];
397-
*
398-
* $map = Convert::listToMap($list, 'id');
399-
*
400-
* print_r($map);
401-
* ```
402-
*
403-
* ```
404-
* Array
405-
* (
406-
* [32] => Array
407-
* (
408-
* [id] => 32
409-
* [name] => Greta
410-
* )
411-
*
412-
* [71] => Array
413-
* (
414-
* [id] => 71
415-
* [name] => Terry
416-
* )
417-
*
418-
* )
419-
* ```
420-
*
421361
* @template T of mixed[]|object
422-
*
423362
* @param array<T> $list
424-
* @param int|string|(Closure(T): (int|string)) $key Either the index or
425-
* property name to use when retrieving keys from arrays or objects in
426-
* `$list`, or a closure that returns a key for each item in `$list`.
363+
* @param int|string $key
427364
* @return array<T>
365+
* @deprecated Use {@see Arr::toMap()} instead
366+
* @codeCoverageIgnore
428367
*/
429368
public static function listToMap(array $list, $key): array
430369
{
431-
return array_combine(
432-
array_map(self::_keyToClosure($key), $list),
433-
$list
434-
);
435-
}
436-
437-
/**
438-
* Get the first item in $list where the value at $key is $value
439-
*
440-
* @template T0 of mixed[]|object
441-
* @template T1
442-
*
443-
* @param iterable<array-key,T0> $list
444-
* @param int|string|(Closure(T0): T1) $key Either the index or property
445-
* name to use when retrieving values from arrays or objects in `$list`, or
446-
* a closure that returns a value for each item in `$list`.
447-
* @param T1 $value
448-
* @return T0|false `false` if no item was found in `$list` with `$value` at
449-
* `$key`.
450-
*/
451-
public static function iterableToItem(iterable $list, $key, $value, bool $strict = false)
452-
{
453-
$list = self::iterableToIterator($list);
454-
$closure = self::_keyToClosure($key);
455-
456-
while ($list->valid()) {
457-
$item = $list->current();
458-
$list->next();
459-
if (($strict && ($closure($item) === $value)) ||
460-
(!$strict && ($closure($item) == $value))) {
461-
return $item;
462-
}
463-
}
464-
465-
return false;
466-
}
467-
468-
/**
469-
* @param int|string|Closure $key
470-
*/
471-
private static function _keyToClosure($key): Closure
472-
{
473-
return $key instanceof Closure
474-
? $key
475-
: fn($item) => self::valueAtKey($item, $key);
476-
}
477-
478-
/**
479-
* Get the value at $key in $item, where $item is an array or object
480-
*
481-
* @param mixed[]|ArrayAccess|object $item
482-
* @param int|string $key
483-
* @return mixed
484-
*/
485-
public static function valueAtKey($item, $key)
486-
{
487-
return is_array($item) || $item instanceof ArrayAccess
488-
? $item[$key]
489-
: $item->$key;
490-
}
491-
492-
/**
493-
* Convert a scalar to a string
494-
*
495-
* @param mixed $value
496-
* @return string|false Returns `false` if `$value` is not a scalar
497-
*/
498-
public static function scalarToString($value)
499-
{
500-
if (is_scalar($value)) {
501-
return (string) $value;
502-
} else {
503-
return false;
504-
}
370+
return Arr::toMap($list, $key);
505371
}
506372

507373
/**

0 commit comments

Comments
 (0)