Skip to content
This repository was archived by the owner on Dec 28, 2023. It is now read-only.

Commit aecf53d

Browse files
committed
Add Enumerable::join(), groupJoin()
1 parent 4176384 commit aecf53d

11 files changed

+780
-20
lines changed

benchmarks/GroupJoinEvent.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Emonkak\Collection\Benchmarks;
4+
5+
use Athletic\AthleticEvent;
6+
use Emonkak\Collection\Collection;
7+
8+
class GroupJoinEvent extends AthleticEvent
9+
{
10+
use CollectionBenchmark;
11+
12+
public function setUp()
13+
{
14+
$this->data = Collection::from([
15+
['id' => 1, 'name' => 'Sumire Uesaka'],
16+
['id' => 2, 'name' => 'Mikako Komatsu'],
17+
['id' => 3, 'name' => 'Rumi okubo'],
18+
['id' => 4, 'name' => 'Natsumi Takamori'],
19+
['id' => 5, 'name' => 'Shiori Mikami'],
20+
])->cycle(10);
21+
$this->tweets = Collection::from([
22+
['user_id' => 1, 'body' => 'foo'],
23+
['user_id' => 1, 'body' => 'bar'],
24+
['user_id' => 1, 'body' => 'baz'],
25+
['user_id' => 3, 'body' => 'hoge'],
26+
['user_id' => 3, 'body' => 'fuga'],
27+
['user_id' => 5, 'body' => 'piyo']
28+
])->cycle(10);
29+
}
30+
31+
protected function execute($xs)
32+
{
33+
$xs->join(
34+
$this->tweets,
35+
function($user) { return $user['id']; },
36+
function($user) { return $user['user_id']; },
37+
function($user, $tweets) {
38+
$user['tweets'] = $tweets;
39+
return $user;
40+
}
41+
)->toList();
42+
}
43+
}

benchmarks/IntercalateEvent.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Emonkak\Collection\Benchmarks;
4+
5+
use Athletic\AthleticEvent;
6+
7+
class IntercalateEvent extends AthleticEvent
8+
{
9+
use CollectionBenchmark;
10+
11+
public function setUp()
12+
{
13+
$this->data = range(0, 1000);
14+
}
15+
16+
/**
17+
* @iterations 100
18+
*/
19+
public function arrayImpl()
20+
{
21+
implode(',', $this->data);
22+
}
23+
24+
protected function execute($xs)
25+
{
26+
$xs->intercalate(',');
27+
}
28+
}

benchmarks/JoinEvent.php

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,40 @@
33
namespace Emonkak\Collection\Benchmarks;
44

55
use Athletic\AthleticEvent;
6+
use Emonkak\Collection\Collection;
67

78
class JoinEvent extends AthleticEvent
89
{
910
use CollectionBenchmark;
1011

1112
public function setUp()
1213
{
13-
$this->data = range(0, 1000);
14-
}
15-
16-
/**
17-
* @iterations 100
18-
*/
19-
public function arrayImpl()
20-
{
21-
implode(',', $this->data);
14+
$this->data = Collection::from([
15+
['id' => 1, 'name' => 'Sumire Uesaka'],
16+
['id' => 2, 'name' => 'Mikako Komatsu'],
17+
['id' => 3, 'name' => 'Rumi okubo'],
18+
['id' => 4, 'name' => 'Natsumi Takamori'],
19+
['id' => 5, 'name' => 'Shiori Mikami'],
20+
])->cycle(10);
21+
$this->users = Collection::from([
22+
['talent_id' => 1, 'user_id' => 139557376],
23+
['talent_id' => 2, 'user_id' => 255386927],
24+
['talent_id' => 2, 'user_id' => 53669663],
25+
['talent_id' => 4, 'user_id' => 2445518118],
26+
['talent_id' => 5, 'user_id' => 199932799]
27+
])->cycle(10);
2228
}
2329

2430
protected function execute($xs)
2531
{
26-
$xs->join(',');
32+
$xs->join(
33+
$this->users,
34+
function($talent) { return $talent['id']; },
35+
function($user) { return $user['talent_id']; },
36+
function($talent, $user) {
37+
$talent['user'] = $user;
38+
return $talent;
39+
}
40+
)->toList();
2741
}
2842
}

src/Enumerable.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,36 @@ public function pluck($property)
212212
});
213213
}
214214

215+
/**
216+
* @param array|\Traversable $inner
217+
* @param mixed $outerKeySelector (outerValue, outerKey, outer) -> joinKey
218+
* @param mixed $innerKeySelector (innerValue, innerKey, outer) -> joinKey
219+
* @param callable $resultValueSelector (outerValue, innerValue) -> resultValue
220+
* @return Collection
221+
*/
222+
public function join($inner, $outerKeySelector, $innerKeySelector, callable $resultValueSelector)
223+
{
224+
$outer = $this->getSource();
225+
$outerKeySelector = $this->resolveSelector($outerKeySelector);
226+
$innerKeySelector = $this->resolveSelector($innerKeySelector);
227+
return $this->newCollection($this->getProvider()->join($outer, $inner, $outerKeySelector, $innerKeySelector, $resultValueSelector));
228+
}
229+
230+
/**
231+
* @param array|\Traversable $inner
232+
* @param mixed $outerKeySelector (outerValue, outerKey, outer) -> joinKey
233+
* @param mixed $innerKeySelector (innerValue, innerKey, outer) -> joinKey
234+
* @param callable $resultValueSelector (outerValue, innerValues[]) -> resultValue
235+
* @return Collection
236+
*/
237+
public function groupJoin($inner, $outerKeySelector, $innerKeySelector, callable $resultValueSelector)
238+
{
239+
$outer = $this->getSource();
240+
$outerKeySelector = $this->resolveSelector($outerKeySelector);
241+
$innerKeySelector = $this->resolveSelector($innerKeySelector);
242+
return $this->newCollection($this->getProvider()->groupJoin($outer, $inner, $outerKeySelector, $innerKeySelector, $resultValueSelector));
243+
}
244+
215245
public function max($selector = null)
216246
{
217247
$xs = $this->getSource();
@@ -693,7 +723,7 @@ public function sortedIndex($value, $selector = null)
693723
return $low;
694724
}
695725

696-
public function join($separator = ',')
726+
public function intercalate($separator = ',')
697727
{
698728
$str = '';
699729
foreach ($this->getSource() as $x) {

src/Iterator/GroupJoinIterator.php

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
3+
namespace Emonkak\Collection\Iterator;
4+
5+
class GroupJoinIterator implements \Iterator
6+
{
7+
/**
8+
* The ourter iterator
9+
*
10+
* @var Iterator
11+
*/
12+
private $outer;
13+
14+
/**
15+
* The inner iterator
16+
*
17+
* @var Iterator
18+
*/
19+
private $inner;
20+
21+
/**
22+
* The outer key selector function
23+
*
24+
* @var callable
25+
*/
26+
private $outerKeySelector;
27+
28+
/**
29+
* The inner key selector function
30+
*
31+
* @var callable
32+
*/
33+
private $innerKeySelector;
34+
35+
/**
36+
* The inner result value selector function
37+
*
38+
* @var callable
39+
*/
40+
private $resultValueSelector;
41+
42+
/**
43+
* @var array
44+
*/
45+
private $lookupTable;
46+
47+
/**
48+
* @var mixed
49+
*/
50+
private $resultValue;
51+
52+
/**
53+
* @param \Iterator $oute
54+
* @param \Iterator $inner
55+
* @param callable $outerKeySelector
56+
* @param callable $innerKeySelector
57+
* @param callable $resultValueSelector
58+
*/
59+
public function __construct(
60+
\Iterator $outer,
61+
\Iterator $inner,
62+
callable $outerKeySelector,
63+
callable $innerKeySelector,
64+
callable $resultValueSelector
65+
)
66+
{
67+
$this->outer = $outer;
68+
$this->inner = $inner;
69+
$this->outerKeySelector = $outerKeySelector;
70+
$this->innerKeySelector = $innerKeySelector;
71+
$this->resultValueSelector = $resultValueSelector;
72+
}
73+
74+
/**
75+
* @see \Iterator
76+
* @return mixed
77+
*/
78+
public function current()
79+
{
80+
return $this->resultValue;
81+
}
82+
83+
/**
84+
* @see \Iterator
85+
* @return mixed
86+
*/
87+
public function key()
88+
{
89+
return $this->outer->key();
90+
}
91+
92+
/**
93+
* @see \Iterator
94+
*/
95+
public function next()
96+
{
97+
$this->outer->next();
98+
$this->fetch();
99+
}
100+
101+
/**
102+
* @see \Iterator
103+
*/
104+
public function rewind()
105+
{
106+
$this->outer->rewind();
107+
$this->lookup();
108+
$this->fetch();
109+
}
110+
111+
/**
112+
* @see \Iterator
113+
* @return boolean
114+
*/
115+
public function valid()
116+
{
117+
return $this->outer->valid();
118+
}
119+
120+
private function fetch()
121+
{
122+
if ($this->outer->valid()) {
123+
$outerValue = $this->outer->current();
124+
$outerKey = $this->outer->key();
125+
$joinKey = call_user_func(
126+
$this->outerKeySelector,
127+
$outerValue,
128+
$outerKey,
129+
$this->outer
130+
);
131+
132+
if (isset($this->lookupTable[$joinKey])) {
133+
$inners = $this->lookupTable[$joinKey];
134+
} else {
135+
$inners = [];
136+
}
137+
138+
$this->resultValue = call_user_func(
139+
$this->resultValueSelector,
140+
$outerValue,
141+
$inners
142+
);
143+
}
144+
}
145+
146+
private function lookup()
147+
{
148+
$this->lookupTable = [];
149+
foreach ($this->inner as $innerKey => $innerValue) {
150+
$joinKey = call_user_func(
151+
$this->innerKeySelector,
152+
$innerValue,
153+
$innerKey,
154+
$this->inner
155+
);
156+
$this->lookupTable[$joinKey][] = $innerValue;;
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)