Skip to content

Commit

Permalink
Introduce new class ipl\Stdlib\Seq
Browse files Browse the repository at this point in the history
  • Loading branch information
nilmerg committed Oct 29, 2021
1 parent 6f8f07c commit 1973833
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
108 changes: 108 additions & 0 deletions src/Seq.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace ipl\Stdlib;

/**
* Collection of utilities for traversables
*/
class Seq
{
/**
* Check if the traversable contains the given needle
*
* @param array|iterable $traversable
* @param mixed $needle Might also be a closure
* @param bool $caseSensitive Whether strings should be compared case-sensitive
*
* @return bool
*/
public static function contains($traversable, $needle, $caseSensitive = true)
{
return self::find($traversable, $needle, $caseSensitive)[0] !== null;
}

/**
* Search in the traversable for the given needle and return its key and value
*
* @param array|iterable $traversable
* @param mixed $needle Might also be a closure
* @param bool $caseSensitive Whether strings should be compared case-sensitive
*
* @return array An array with two entries, the first is the key, then the value. Both are null if nothing is found.
*/
public static function find($traversable, $needle, $caseSensitive = true)
{
$usesCallback = is_callable($needle);
if (! $usesCallback && $caseSensitive && is_array($traversable)) {
return [array_search($needle, $traversable, true), $needle];
}

if (! $caseSensitive && is_string($needle) && ! $usesCallback) {
$needle = strtolower($needle);
}

foreach ($traversable as $key => $item) {
$originalItem = $item;
if (! $caseSensitive && is_string($item)) {
$item = strtolower($item);
}

if ($usesCallback && $needle($item)) {
return [$key, $originalItem];
} elseif ($item === $needle) {
return [$key, $originalItem];
}
}

return [null, null];
}

/**
* Search in the traversable for the given needle and return its key
*
* @param array|iterable $traversable
* @param mixed $needle Might also be a closure
* @param bool $caseSensitive Whether strings should be compared case-sensitive
*
* @return mixed|null Null if nothing is found
*/
public static function findKey($traversable, $needle, $caseSensitive = true)
{
return self::find($traversable, $needle, $caseSensitive)[0];
}

/**
* Search in the traversable for the given needle and return its value
*
* @param array|iterable $traversable
* @param mixed $needle Might also be a closure
* @param bool $caseSensitive Whether strings should be compared case-sensitive
*
* @return mixed|null Null if nothing is found
*/
public static function findValue($traversable, $needle, $caseSensitive = true)
{
$usesCallback = is_callable($needle);
if (! $usesCallback && $caseSensitive && is_array($traversable)) {
return isset($traversable[$needle]) ? $traversable[$needle] : null;
}

if (! $caseSensitive && is_string($needle) && ! $usesCallback) {
$needle = strtolower($needle);
}

foreach ($traversable as $key => $item) {
if (! $caseSensitive && is_string($key)) {
$key = strtolower($key);
}

if ($usesCallback && $needle($key)) {
return $item;
} elseif ($key === $needle) {
return $item;
}
}

return null;
}
}
90 changes: 90 additions & 0 deletions tests/SeqTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace ipl\Tests\Stdlib;

use ArrayIterator;
use ipl\Stdlib\Seq;

class SeqTest extends TestCase
{
public function testFindWithArrays()
{
$this->assertEquals(
['oof', 'BAR'],
Seq::find(['foo' => 'bar', 'oof' => 'BAR'], 'BAR')
);
$this->assertEquals(
['foo', 'bar'],
Seq::find(['foo' => 'bar', 'oof' => 'BAR'], 'BAR', false)
);
}

public function testFindWithGenerators()
{
$generatorCreator = function () {
yield 'foo' => 'bar';
yield 'oof' => 'BAR';
};

$this->assertEquals(
['oof', 'BAR'],
Seq::find($generatorCreator(), 'BAR')
);
$this->assertEquals(
['foo', 'bar'],
Seq::find($generatorCreator(), 'BAR', false)
);
}

public function testFindWithIterators()
{
$this->assertEquals(
['oof', 'BAR'],
Seq::find(new ArrayIterator(['foo' => 'bar', 'oof' => 'BAR']), 'BAR')
);
$this->assertEquals(
['foo', 'bar'],
Seq::find(new ArrayIterator(['foo' => 'bar', 'oof' => 'BAR']), 'BAR', false)
);
}
public function testFindValueWithArrays()
{
$this->assertEquals(
'BAR',
Seq::findValue(['foo' => 'bar', 'FOO' => 'BAR'], 'FOO')
);
$this->assertEquals(
'bar',
Seq::findValue(['foo' => 'bar', 'FOO' => 'BAR'], 'FOO', false)
);
}

public function testFindValueWithGenerators()
{
$generatorCreator = function () {
yield 'foo' => 'bar';
yield 'FOO' => 'BAR';
};

$this->assertEquals(
'BAR',
Seq::findValue($generatorCreator(), 'FOO')
);
$this->assertEquals(
'bar',
Seq::findValue($generatorCreator(), 'FOO', false)
);
}

public function testFindValueWithIterators()
{
$this->assertEquals(
'BAR',
Seq::findValue(new ArrayIterator(['foo' => 'bar', 'FOO' => 'BAR']), 'FOO')
);
$this->assertEquals(
'bar',
Seq::findValue(new ArrayIterator(['foo' => 'bar', 'FOO' => 'BAR']), 'FOO', false)
);
}
}

0 comments on commit 1973833

Please sign in to comment.