Skip to content

Commit 79a1cf5

Browse files
authored
Merge pull request #28181 from timacdonald/collection_duplicates
[5.8] Add duplicates method to collection
2 parents 627fa08 + b86a424 commit 79a1cf5

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

src/Illuminate/Support/Collection.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,50 @@ public function diffKeysUsing($items, callable $callback)
407407
return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
408408
}
409409

410+
/**
411+
* Retrieve duplicate items from the collection.
412+
*
413+
* @param callable|null $callback
414+
* @param bool $strict
415+
* @return static
416+
*/
417+
public function duplicates($callback = null, $strict = false)
418+
{
419+
$items = $this->map($this->valueRetriever($callback));
420+
421+
$uniqueItems = $items->unique(null, $strict);
422+
423+
$compare = $strict ? function ($a, $b) {
424+
return $a === $b;
425+
}
426+
: function ($a, $b) {
427+
return $a == $b;
428+
};
429+
430+
$duplicates = new static;
431+
432+
foreach ($items as $key => $value) {
433+
if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
434+
$uniqueItems->shift();
435+
} else {
436+
$duplicates[$key] = $value;
437+
}
438+
}
439+
440+
return $duplicates;
441+
}
442+
443+
/**
444+
* Retrieve duplicate items from the collection using strict comparison.
445+
*
446+
* @param callable|null $callback
447+
* @return static
448+
*/
449+
public function duplicatesStrict($callback = null)
450+
{
451+
return $this->duplicates($callback, true);
452+
}
453+
410454
/**
411455
* Execute a callback over each item.
412456
*

tests/Support/SupportCollectionTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,60 @@ public function testDiffAssocUsing()
745745
$this->assertEquals(['b' => 'brown', 'c' => 'blue', 'red'], $c1->diffAssocUsing($c2, 'strcasecmp')->all());
746746
}
747747

748+
public function testDuplicates()
749+
{
750+
$duplicates = Collection::make([1, 2, 1, 'laravel', null, 'laravel', 'php', null])->duplicates()->all();
751+
$this->assertSame([2 => 1, 5 => 'laravel', 7 => null], $duplicates);
752+
753+
// does loose comparison
754+
$duplicates = Collection::make([2, '2', [], null])->duplicates()->all();
755+
$this->assertSame([1 => '2', 3 => null], $duplicates);
756+
757+
// works with mix of primitives
758+
$duplicates = Collection::make([1, '2', ['laravel'], ['laravel'], null, '2'])->duplicates()->all();
759+
$this->assertSame([3 => ['laravel'], 5 => '2'], $duplicates);
760+
761+
// works with mix of objects and primitives **excepts numbers**.
762+
$expected = new Collection(['laravel']);
763+
$duplicates = Collection::make([new Collection(['laravel']), $expected, $expected, [], '2', '2'])->duplicates()->all();
764+
$this->assertSame([1 => $expected, 2 => $expected, 5 => '2'], $duplicates);
765+
}
766+
767+
public function testDuplicatesWithKey()
768+
{
769+
$items = [['framework' => 'vue'], ['framework' => 'laravel'], ['framework' => 'laravel']];
770+
$duplicates = Collection::make($items)->duplicates('framework')->all();
771+
$this->assertSame([2 => 'laravel'], $duplicates);
772+
}
773+
774+
public function testDuplicatesWithCallback()
775+
{
776+
$items = [['framework' => 'vue'], ['framework' => 'laravel'], ['framework' => 'laravel']];
777+
$duplicates = Collection::make($items)->duplicates(function ($item) {
778+
return $item['framework'];
779+
})->all();
780+
$this->assertSame([2 => 'laravel'], $duplicates);
781+
}
782+
783+
public function testDuplicatesWithStrict()
784+
{
785+
$duplicates = Collection::make([1, 2, 1, 'laravel', null, 'laravel', 'php', null])->duplicatesStrict()->all();
786+
$this->assertSame([2 => 1, 5 => 'laravel', 7 => null], $duplicates);
787+
788+
// does strict comparison
789+
$duplicates = Collection::make([2, '2', [], null])->duplicatesStrict()->all();
790+
$this->assertSame([], $duplicates);
791+
792+
// works with mix of primitives
793+
$duplicates = Collection::make([1, '2', ['laravel'], ['laravel'], null, '2'])->duplicatesStrict()->all();
794+
$this->assertSame([3 => ['laravel'], 5 => '2'], $duplicates);
795+
796+
// works with mix of primitives, objects, and numbers
797+
$expected = new Collection(['laravel']);
798+
$duplicates = Collection::make([new Collection(['laravel']), $expected, $expected, [], '2', '2'])->duplicatesStrict()->all();
799+
$this->assertSame([2 => $expected, 5 => '2'], $duplicates);
800+
}
801+
748802
public function testEach()
749803
{
750804
$c = new Collection($original = [1, 2, 'foo' => 'bar', 'bam' => 'baz']);

0 commit comments

Comments
 (0)