Skip to content

Commit 659683f

Browse files
authored
Merge pull request #4 from adhocore/develop
Reorganize code Add more functionalities in base/array/collection 100% test coverage Docblock documentation
2 parents 4f8bcfc + af176c5 commit 659683f

11 files changed

+1043
-168
lines changed

src/Underscore.php

+72-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,77 @@
22

33
namespace Ahc\Underscore;
44

5-
final class Underscore extends UnderscoreCollection
5+
final class Underscore extends UnderscoreArray
66
{
7+
/**
8+
* Returns a callable which when invoked caches the result for given arguments
9+
* and reuses that result in subsequent calls.
10+
*
11+
* @param callable $fn The main callback.
12+
*
13+
* @return mixed
14+
*/
15+
public function memoize(callable $fn)
16+
{
17+
static $memo = [];
18+
19+
return function () use (&$memo, $fn) {
20+
$hash = \md5(\json_encode($args = \func_get_args()));
21+
22+
if (isset($memo[$hash])) {
23+
return $memo[$hash];
24+
}
25+
26+
return $memo[$hash] = \call_user_func_array($fn, $args);
27+
};
28+
}
29+
30+
/**
31+
* Cache the result of callback for given arguments and reuse that in subsequent call.
32+
*
33+
* @param callable $fn The main callback.
34+
* @param int $wait The time to wait in millisec.
35+
*
36+
* @return mixed
37+
*/
38+
public function delay(callable $fn, $wait)
39+
{
40+
return function () use ($fn, $wait) {
41+
\usleep(1000 * $wait);
42+
43+
return \call_user_func_array($fn, \func_get_args());
44+
};
45+
}
46+
47+
/**
48+
* Returns a callable that wraps given callable which can be only invoked
49+
* at most once per given $wait threshold.
50+
*
51+
* @param callable $fn The main callback.
52+
* @param int $wait The callback will only be triggered at most once within this period.
53+
*
54+
* @return mixed The return set of callback if runnable else the previous cache.
55+
*/
56+
public function throttle(callable $fn, $wait)
57+
{
58+
static $previous = 0;
59+
static $result = null;
60+
61+
return function () use ($fn, &$previous, &$result, &$wait) {
62+
$now = $this->now();
63+
64+
if (!$previous) {
65+
$previous = $now;
66+
}
67+
68+
$remaining = $wait - ($now - $previous);
69+
70+
if ($remaining <= 0 || $remaining > $wait) {
71+
$previous = $now;
72+
$result = \call_user_func_array($fn, \func_get_args());
73+
}
74+
75+
return $result;
76+
};
77+
}
778
}

src/UnderscoreArray.php

+243-16
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,277 @@
22

33
namespace Ahc\Underscore;
44

5-
class UnderscoreArray extends UnderscoreBase
5+
class UnderscoreArray extends UnderscoreCollection
66
{
7-
public function first()
7+
/**
8+
* Get the first n items.
9+
*
10+
* @param int $n
11+
*
12+
* @return array
13+
*/
14+
public function first($n = 1)
815
{
9-
return \reset($this->data);
16+
return $this->slice($n, true);
1017
}
1118

12-
public function last()
19+
/**
20+
* Alias of first().
21+
*/
22+
public function head($n = 1)
1323
{
14-
return \end($this->data);
24+
return $this->first($n);
1525
}
1626

27+
/**
28+
* Alias of first().
29+
*/
30+
public function take($n = 1)
31+
{
32+
return $this->first($n);
33+
}
34+
35+
/**
36+
* Get the last n items.
37+
*
38+
* @param int $n
39+
*
40+
* @return array
41+
*/
42+
public function last($n = 1)
43+
{
44+
return $this->slice($n, false);
45+
}
46+
47+
/**
48+
* Alias of last().
49+
*/
50+
public function tail($n = 1)
51+
{
52+
return $this->last($n);
53+
}
54+
55+
/**
56+
* Alias of last().
57+
*/
58+
public function drop($n = 1)
59+
{
60+
return $this->last($n);
61+
}
62+
63+
/**
64+
* Extracts n items from first or last.
65+
*
66+
* @internal
67+
*
68+
* @param int $n
69+
* @param bool $isFirst From first if true, else last.
70+
*
71+
* @return array
72+
*/
73+
protected function slice($n, $isFirst = true)
74+
{
75+
if ($n < 2) {
76+
return $isFirst ? \reset($this->data) : \end($this->data);
77+
}
78+
79+
if ($n >= $c = $this->count()) {
80+
return $this->data;
81+
}
82+
83+
return \array_slice($this->data, $isFirst ? 0 : $c - $n, $isFirst ? $n : null, true);
84+
}
85+
86+
/**
87+
* Get only the truthy items.
88+
*
89+
* @return self
90+
*/
1791
public function compact()
1892
{
19-
return new static(\array_filter($this->data));
93+
return $this->filter(null);
2094
}
2195

96+
/**
97+
* Gets the flattened version of multidimensional items.
98+
*
99+
* @return self
100+
*/
22101
public function flatten()
23102
{
24103
return new static($this->flat($this->data));
25104
}
26105

106+
/**
107+
* Gets the unique items using the id resulted from callback.
108+
*
109+
* @param callback|string $fn The callback. String is resolved to value of that index.
110+
*
111+
* @return self
112+
*/
27113
public function unique($fn = null)
28114
{
29115
if (null === $fn) {
30116
return new static(\array_unique($this->data));
31117
}
32118

33-
$ids = $data = [];
119+
$ids = [];
34120
$fn = $this->valueFn($fn);
35121

36-
foreach ($this->data as $index => $value) {
37-
if (!isset($ids[$id = $fn($value, $index)])) {
38-
$ids[$id] = true;
122+
return $this->filter(function ($value, $index) use ($fn, &$ids) {
123+
return !isset($ids[$id = $fn($value, $index)]) ? $ids[$id] = true : false;
124+
});
125+
}
39126

40-
$data[$index] = $value;
41-
}
42-
}
127+
/**
128+
* Alias of unique().
129+
*/
130+
public function uniq($fn = null)
131+
{
132+
return $this->unique($fn);
133+
}
43134

44-
return new static($data);
135+
/**
136+
* Get the items whose value is not in given data.
137+
*
138+
* @param array|mixed $data Array or array like or array convertible.
139+
*
140+
* @return self
141+
*/
142+
public function difference($data)
143+
{
144+
$data = $this->asArray($data);
145+
146+
return $this->filter(function ($value) use ($data) {
147+
return !\in_array($value, $data);
148+
});
45149
}
46150

47-
public function uniq($fn)
151+
/**
152+
* Alias of without().
153+
*/
154+
public function without($data)
48155
{
49-
return $this->unique($fn);
156+
return $this->difference($data);
157+
}
158+
159+
/**
160+
* Get the union/merger of items with given data.
161+
*
162+
* @param array|mixed $data Array or array like or array convertible.
163+
*
164+
* @return self
165+
*/
166+
public function union($data)
167+
{
168+
return new static(\array_merge($this->data, $this->asArray($data)));
169+
}
170+
171+
/**
172+
* Gets the items whose value is common with given data.
173+
*
174+
* @param array|mixed $data Array or array like or array convertible.
175+
*
176+
* @return self
177+
*/
178+
public function intersection($data)
179+
{
180+
$data = $this->asArray($data);
181+
182+
return $this->filter(function ($value) use ($data) {
183+
return \in_array($value, $data);
184+
});
185+
}
186+
187+
/**
188+
* Group the values from data and items having same indexes together.
189+
*
190+
* @param array|mixed $data Array or array like or array convertible.
191+
*
192+
* @return self
193+
*/
194+
public function zip($data)
195+
{
196+
$data = $this->asArray($data);
197+
198+
return $this->map(function ($value, $index) use ($data) {
199+
return [$value, isset($data[$index]) ? $data[$index] : null];
200+
});
201+
}
202+
203+
/**
204+
* Hydrate the items into given class or stdClass.
205+
*
206+
* @param string $className FQCN of the class whose constructor accepts two parameters: value and index.
207+
*
208+
* @return self
209+
*/
210+
public function object($className = null)
211+
{
212+
return $this->map(function ($value, $index) use ($className) {
213+
return $className ? new $className($value, $index) : (object) \compact('value', 'index');
214+
});
215+
}
216+
217+
/**
218+
* Find the first index that passes given truth test.
219+
*
220+
* @param callable $fn The truth test callback.
221+
*
222+
* @return mixed|null
223+
*/
224+
public function firstIndex($fn = null)
225+
{
226+
return $this->find($this->valueFn($fn), false);
227+
}
228+
229+
/**
230+
* Find the larst index that passes given truth test.
231+
*
232+
* @param callable $fn The truth test callback.
233+
*
234+
* @return mixed|null
235+
*/
236+
public function lastIndex($fn = null)
237+
{
238+
return (new static(\array_reverse($this->data, true)))->find($this->valueFn($fn), false);
239+
}
240+
241+
/**
242+
* Find the first index of given value if available null otherwise.
243+
*
244+
* @param callable $fn The truth test callback.
245+
*
246+
* @return string|int|null
247+
*/
248+
public function indexOf($value)
249+
{
250+
return (false === $index = \array_search($value, $this->data)) ? null : $index;
251+
}
252+
253+
/**
254+
* Find the last index of given value if available null otherwise.
255+
*
256+
* @param callable $fn The truth test callback.
257+
*
258+
* @return string|int|null
259+
*/
260+
public function lastIndexOf($value)
261+
{
262+
return (false === $index = \array_search($value, \array_reverse($this->data, true))) ? null : $index;
263+
}
264+
265+
/**
266+
* Creates a new range from start to stop with given step.
267+
*
268+
* @param int $start
269+
* @param int $stop
270+
* @param int $step
271+
*
272+
* @return self
273+
*/
274+
public function range($start, $stop, $step = 1)
275+
{
276+
return new static(\range($start, $stop, $step));
50277
}
51278
}

0 commit comments

Comments
 (0)