Skip to content

Commit

Permalink
[Dict] add intersect and diff functions
Browse files Browse the repository at this point in the history
  • Loading branch information
azjezz committed Feb 20, 2021
1 parent b33d6b2 commit 84513c6
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/Psl/Dict/diff.php
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Psl\Dict;

use Psl\Iter;
use Psl\Vec;

use function array_diff;

/**
* Computes the difference of iterables.
*
* @psalm-template Tk of array-key
* @psalm-template Tv
*
* @psalm-param iterable<Tk, Tv> $first
* @psalm-param iterable<Tk, Tv> $second
* @psalm-param iterable<Tk, Tv> ...$rest
*
* @psalm-return array<Tk, Tv>
*/
function diff(iterable $first, iterable $second, iterable ...$rest): array
{
if (Iter\is_empty($first)) {
return [];
}

return array_diff(from_iterable($first), from_iterable($second), ...Vec\map(
$rest,
/**
* @template Tk of array-key
* @template Tv
*
* @param iterable<Tk, Tv> $iterable
*
* @return array<Tk, Tv>
*/
static fn(iterable $iterable): array => from_iterable($iterable)
));
}
42 changes: 42 additions & 0 deletions src/Psl/Dict/diff_by_key.php
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Psl\Dict;

use Psl\Iter;
use Psl\Vec;

use function array_diff_key;

/**
* Computes the difference of iterables using keys for comparison.
*
* @psalm-template Tk of array-key
* @psalm-template Tv
*
* @psalm-param iterable<Tk, Tv> $first
* @psalm-param iterable<Tk, mixed> $second
* @psalm-param iterable<Tk, mixed> ...$rest
*
* @psalm-return array<Tk, Tv>
*/
function diff_by_key(iterable $first, iterable $second, iterable ...$rest): array
{
if (Iter\is_empty($first)) {
return [];
}

return array_diff_key(from_iterable($first), from_iterable($second), ...Vec\map(
$rest,
/**
* @template Tk of array-key
* @template Tv
*
* @param iterable<Tk, Tv> $iterable
*
* @return array<Tk, Tv>
*/
static fn(iterable $iterable): array => from_iterable($iterable)
));
}
42 changes: 42 additions & 0 deletions src/Psl/Dict/intersect.php
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Psl\Dict;

use Psl\Iter;
use Psl\Vec;

use function array_intersect;

/**
* Computes the intersection of iterables.
*
* @template Tk of array-key
* @template Tv
*
* @param iterable<Tk, Tv> $first
* @param iterable<Tk, mixed> $second
* @param iterable<Tk, mixed> ...$rest
*
* @return array<Tk, Tv>
*/
function intersect(iterable $first, iterable $second, iterable ...$rest): array
{
if (Iter\is_empty($first)) {
return [];
}

return array_intersect(from_iterable($first), from_iterable($second), ...Vec\map(
$rest,
/**
* @template Tk of array-key
* @template Tv
*
* @param iterable<Tk, Tv> $iterable
*
* @return array<Tk, Tv>
*/
static fn(iterable $iterable): array => from_iterable($iterable)
));
}
42 changes: 42 additions & 0 deletions src/Psl/Dict/intersect_by_key.php
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Psl\Dict;

use Psl\Iter;
use Psl\Vec;

use function array_intersect_key;

/**
* Computes the intersection of iterables using keys for comparison.
*
* @template Tk of array-key
* @template Tv
*
* @param iterable<Tk, Tv> $first
* @param iterable<Tk, mixed> $second
* @param iterable<Tk, mixed> ...$rest
*
* @return array<Tk, Tv>
*/
function intersect_by_key(iterable $first, iterable $second, iterable ...$rest): array
{
if (Iter\is_empty($first)) {
return [];
}

return array_intersect_key(from_iterable($first), from_iterable($second), ...Vec\map(
$rest,
/**
* @template Tk of array-key
* @template Tv
*
* @param iterable<Tk, Tv> $iterable
*
* @return array<Tk, Tv>
*/
static fn(iterable $iterable): array => from_iterable($iterable)
));
}
4 changes: 4 additions & 0 deletions src/Psl/Internal/Loader.php
Expand Up @@ -129,6 +129,10 @@ final class Loader
'Psl\Dict\take_while',
'Psl\Dict\unique',
'Psl\Dict\unique_by',
'Psl\Dict\diff',
'Psl\Dict\diff_by_key',
'Psl\Dict\intersect',
'Psl\Dict\intersect_by_key',
'Psl\Fun\after',
'Psl\Fun\identity',
'Psl\Fun\pipe',
Expand Down
5 changes: 5 additions & 0 deletions src/Psl/Iter/chain.php
Expand Up @@ -5,6 +5,7 @@
namespace Psl\Iter;

use Generator;
use Psl\Vec;

/**
* Chains the iterables that were passed as arguments.
Expand All @@ -23,6 +24,10 @@
* @psalm-param iterable<Tk, Tv> ...$iterables Iterables to chain
*
* @psalm-return Iterator<Tk, Tv>
*
* @deprecated use `Vec\concat` instead.
*
* @see Vec\concat()
*/
function chain(iterable ...$iterables): Iterator
{
Expand Down
5 changes: 5 additions & 0 deletions src/Psl/Iter/diff_by_key.php
Expand Up @@ -5,6 +5,7 @@
namespace Psl\Iter;

use Generator;
use Psl\Dict;

/**
* @psalm-template Tk
Expand All @@ -15,6 +16,10 @@
* @psalm-param iterable<Tk, mixed> ...$rest
*
* @psalm-return Iterator<Tk, Tv>
*
* @deprecated use `Dict\diff_by_key` instead.
*
* @see Dict\diff_by_key()
*/
function diff_by_key(iterable $first, iterable $second, iterable ...$rest): Iterator
{
Expand Down
34 changes: 34 additions & 0 deletions tests/Psl/Dict/DiffByKeyTest.php
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\Dict;

use PHPUnit\Framework\TestCase;
use Psl\Dict;
use Psl\Vec;

final class DiffByKeyTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testDiffByKey(array $expected, iterable $first, iterable $second, iterable ...$rest): void
{
static::assertSame($expected, Dict\diff_by_key($first, $second, ...$rest));
}

public function provideData(): iterable
{
yield [[], [], [], []];
yield [[], [], [1, 2, 3]];
yield [[], [], [1, 2, 3], []];
yield [[], [], [1, 2, 3], [], [4, 5]];

yield [[1, 2], [1, 2], [], []];
yield [[1, 2], [1, 2], ['foo' => 2], []];
yield [[1, 2], [1, 2], [], ['baz' => 1]];
yield [[6 => 7, 7 => 8], Vec\range(1, 8), Vec\range(1, 6), []];
yield [[7 => 8], Vec\range(1, 8), Vec\range(1, 6), [6 => 7]];
}
}
36 changes: 36 additions & 0 deletions tests/Psl/Dict/DiffTest.php
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\Dict;

use PHPUnit\Framework\TestCase;
use Psl\Dict;
use Psl\Vec;

final class DiffTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testDiff(array $expected, iterable $first, iterable $second, iterable ...$rest): void
{
static::assertSame($expected, Dict\diff($first, $second, ...$rest));
}

public function provideData(): iterable
{
yield [[], [], [], []];
yield [[], [], [1, 2, 3]];
yield [[], [], [1, 2, 3], []];
yield [[], [], [1, 2, 3], [], [4, 5]];

yield [[1, 2], [1, 2], [], []];
yield [[1], [1, 2], ['foo' => 2], []];
yield [[1 => 2], [1, 2], [], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2], ['baz' => 1]];
yield [[6 => 7, 7 => 8], Vec\range(1, 8), Vec\range(1, 6), []];
yield [[7 => 8], Vec\range(1, 8), Vec\range(1, 6), [6 => 7]];
}
}
38 changes: 38 additions & 0 deletions tests/Psl/Dict/IntersectByKeyTest.php
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\Dict;

use PHPUnit\Framework\TestCase;
use Psl\Dict;
use Psl\Vec;

final class IntersectByKeyTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testIntersectByKey(array $expected, iterable $first, iterable $second, iterable ...$rest): void
{
static::assertSame($expected, Dict\intersect_by_key($first, $second, ...$rest));
}

public function provideData(): iterable
{
yield [[], [], [], []];
yield [[], [], [1, 2, 3]];
yield [[], [], [1, 2, 3], []];
yield [[], [], [1, 2, 3], [], [4, 5]];

yield [[], [1, 2], [], []];
yield [[], [1, 2], ['foo' => 2], []];
yield [[], [1, 2], [], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2, 'baz' => 1]];
yield [[1, 2, 3, 4, 5, 6], Vec\range(1, 8), Vec\range(1, 6)];
yield [[], Vec\range(1, 8), Vec\range(1, 6), []];
yield [[5 => 6], Vec\range(1, 8), Vec\range(1, 6), [5 => 6, 6 => 7]];
}
}
38 changes: 38 additions & 0 deletions tests/Psl/Dict/IntersectTest.php
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\Dict;

use PHPUnit\Framework\TestCase;
use Psl\Dict;
use Psl\Vec;

final class IntersectTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testIntersect(array $expected, iterable $first, iterable $second, iterable ...$rest): void
{
static::assertSame($expected, Dict\intersect($first, $second, ...$rest));
}

public function provideData(): iterable
{
yield [[], [], [], []];
yield [[], [], [1, 2, 3]];
yield [[], [], [1, 2, 3], []];
yield [[], [], [1, 2, 3], [], [4, 5]];

yield [[], [1, 2], [], []];
yield [[], [1, 2], ['foo' => 2], []];
yield [[], [1, 2], [], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2], ['baz' => 1]];
yield [[], [1, 2], ['foo' => 2], ['baz' => 1]];
yield [[1, 2], [1, 2], ['foo' => 2, 'baz' => 1]];
yield [[1, 2, 3, 4, 5, 6], Vec\range(1, 8), Vec\range(1, 6)];
yield [[], Vec\range(1, 8), Vec\range(1, 6), []];
yield [[5 => 6], Vec\range(1, 8), Vec\range(1, 6), [5 => 6, 6 => 7]];
}
}

0 comments on commit 84513c6

Please sign in to comment.