1: <?php
2:
3: namespace Chromabits\Nucleus\Data\Traits;
4:
5: use Chromabits\Nucleus\Control\Interfaces\ApplyInterface;
6: use Chromabits\Nucleus\Control\Maybe;
7: use Chromabits\Nucleus\Data\ArrayList;
8: use Chromabits\Nucleus\Data\Interfaces\FunctorInterface;
9: use Chromabits\Nucleus\Data\Interfaces\ListInterface;
10: use Chromabits\Nucleus\Data\Interfaces\MonoidInterface;
11: use Chromabits\Nucleus\Data\Iterable;
12: use Chromabits\Nucleus\Exceptions\CoreException;
13: use Chromabits\Nucleus\Exceptions\MindTheGapException;
14: use Chromabits\Nucleus\Meditation\Arguments;
15: use Chromabits\Nucleus\Meditation\Boa;
16: use Chromabits\Nucleus\Meditation\Constraints\AbstractTypeConstraint;
17: use Chromabits\Nucleus\Meditation\Exceptions\InvalidArgumentException;
18: use Chromabits\Nucleus\Support\Std;
19:
20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
31: trait ArrayBackingTrait
32: {
33: use SameTypeTrait;
34:
35: 36: 37:
38: protected abstract function getKeyType();
39:
40: 41: 42: 43: 44:
45: protected abstract function map(callable $function);
46:
47: 48: 49: 50: 51: 52: 53:
54: public function fmap(callable $closure)
55: {
56: return static::of(Std::map($closure, $this->value));
57: }
58:
59: 60: 61: 62: 63: 64: 65: 66:
67: public function foldr(callable $closure, $initial)
68: {
69: return $this->reverse()->foldl(
70: function ($acc, $x) use ($closure) {
71: return $closure($x, $acc);
72: },
73: $initial
74: );
75: }
76:
77: 78: 79: 80: 81: 82: 83: 84:
85: public function foldrWithKeys(callable $closure, $initial)
86: {
87: return $this
88: ->reverse()
89: ->foldlWithKeys(
90: function ($acc, $key, $x) use ($closure) {
91: return $closure($key, $x, $acc);
92: },
93: $initial
94: );
95: }
96:
97: 98: 99: 100: 101: 102: 103: 104:
105: public function foldl(callable $closure, $initial)
106: {
107: return array_reduce($this->value, $closure, $initial);
108: }
109:
110: 111: 112: 113: 114: 115: 116: 117:
118: public function foldlWithKeys(callable $callback, $initial)
119: {
120: $accumulator = $initial;
121:
122: foreach ($this->value as $key => $value) {
123: $accumulator = $callback($accumulator, $key, $value);
124: }
125:
126: return $accumulator;
127: }
128:
129: 130: 131: 132: 133: 134: 135: 136:
137: public function slice($begin, $end = null)
138: {
139: Arguments::define(
140: Boa::integer(),
141: Boa::either(Boa::null(), Boa::integer())
142: )->check($begin, $end);
143:
144: if ($end === null) {
145: return static::of(array_slice($this->value, $begin));
146: }
147:
148: $actualBegin = $begin;
149: $actualEnd = $end;
150:
151: if ($begin < 0) {
152: $actualBegin = $this->size - $begin;
153: }
154:
155: if ($end < 0) {
156: $actualEnd = $this->size - $end;
157: }
158:
159: $diff = $actualEnd - $actualBegin;
160:
161: if ($diff < 0) {
162: throw new CoreException('Invalid range.');
163: }
164:
165: return static::of(
166: array_slice(
167: $this->value,
168: $actualBegin,
169: $diff
170: )
171: );
172: }
173:
174: 175: 176: 177: 178:
179: public function takeWhile(callable $predicate)
180: {
181: $taken = [];
182:
183: foreach ($this->value as $key => $value) {
184: if ($predicate($value, $key, $this)) {
185: $taken[] = $value;
186: }
187: }
188:
189: return static::of($taken);
190: }
191:
192: 193: 194: 195: 196:
197: public function ap(ApplyInterface $other)
198: {
199: $this->assertSameType($other);
200:
201: $result = [];
202:
203: Std::poll(
204: function ($ii) use (&$result, &$other) {
205: Std::poll(
206: function ($jj) use (&$result, &$other, $ii) {
207: $result[] = Std::call(
208: $this->value[$ii],
209: $other->value[$jj]
210: );
211: },
212: count($other->value)
213: );
214: },
215: count($this->value)
216: );
217:
218: return $result;
219: }
220:
221: 222: 223: 224: 225: 226: 227: 228:
229: public function lookup($key)
230: {
231: Arguments::define($this->getKeyType())->check($key);
232:
233: if (!$this->member($key)) {
234: return Maybe::nothing();
235: }
236:
237: return Maybe::just($this->value[$key]);
238: }
239:
240: 241: 242: 243: 244: 245: 246:
247: public function member($key)
248: {
249: return array_key_exists($key, $this->value);
250: }
251:
252: 253: 254: 255: 256:
257: public function lookupIn($searchKeyPath)
258: {
259: $path = ArrayList::of($searchKeyPath);
260:
261: if ($path->count() === 0) {
262: return Maybe::nothing();
263: }
264:
265: if ($path->count() === 1) {
266: return $this->lookup($path->head());
267: }
268:
269: $value = $this->lookup($path->head());
270:
271: if ($value->isNothing()) {
272: return $value;
273: }
274:
275: $innerValue = Maybe::fromJust($value);
276:
277: if ($innerValue instanceof Iterable) {
278: return $innerValue->lookupIn($path->tail());
279: }
280:
281: return Maybe::nothing();
282: }
283:
284: 285: 286: 287: 288: 289:
290: public function memberIn($searchKeyPath)
291: {
292: $path = ArrayList::of($searchKeyPath);
293:
294: if ($path->count() === 0) {
295: return false;
296: }
297:
298: if ($path->count() === 1) {
299: return $this->member($path->head());
300: }
301:
302: if ($this->member($path->head()) === false) {
303: return false;
304: }
305:
306: $innerValue = Maybe::fromJust($this->lookup($path->head()));
307:
308: if ($innerValue instanceof Iterable) {
309: return $innerValue->memberIn($path->tail());
310: }
311:
312: return false;
313: }
314:
315: 316: 317: 318: 319:
320: public function sort(callable $comparator = null)
321: {
322: $copy = array_merge($this->value);
323:
324: if ($comparator === null) {
325: return static::of(sort($copy));
326: }
327:
328: return static::of(usort($copy, $comparator));
329: }
330:
331: 332: 333: 334: 335:
336: public function each(callable $sideEffect)
337: {
338: $count = 0;
339:
340: foreach ($this->value as $key => $value) {
341: $count++;
342:
343: if ($sideEffect($value, $key, $this) === false) {
344: return $count;
345: }
346: }
347:
348: return $count;
349: }
350:
351: 352: 353: 354: 355: 356: 357: 358:
359: public function delete($key)
360: {
361: Arguments::define($this->getKeyType())->check($key);
362:
363: $cloned = array_merge($this->value);
364:
365: unset($cloned[$key]);
366:
367: return static::of($cloned);
368: }
369:
370: 371: 372: 373: 374: 375: 376: 377:
378: public function exceptValues($excluded = [])
379: {
380: Arguments::define(
381: Boa::arrOf(
382: Boa::either(
383: Boa::string(),
384: Boa::integer()
385: )
386: )
387: )->check($excluded);
388:
389: return $this->filter(
390: function ($value) use ($excluded) {
391: return !in_array($value, $excluded);
392: }
393: );
394: }
395:
396: 397: 398: 399: 400:
401: public function unique($sortFlags = null)
402: {
403: return static::of(array_unique($this->value, $sortFlags));
404: }
405:
406: 407: 408:
409: public function keys()
410: {
411: return new ArrayList(array_keys($this->value));
412: }
413:
414: 415: 416: 417: 418:
419: public function join($glue = '')
420: {
421: return implode($glue, $this->value);
422: }
423:
424: 425: 426: 427: 428: 429: 430:
431: public function only($included = [])
432: {
433: Arguments::define(
434: Boa::either(
435: Boa::arrOf(
436: Boa::either(
437: Boa::string(),
438: Boa::integer()
439: )
440: ),
441: Boa::null()
442: )
443: )->check($included);
444:
445: if (is_null($included)) {
446: return $this;
447: }
448:
449: if (count($included) == 0) {
450: return static::zero();
451: }
452:
453: return static::of(
454: array_intersect_key($this->value, array_flip($included))
455: );
456: }
457:
458: 459: 460: 461: 462:
463: public static function zero()
464: {
465: return static::of([]);
466: }
467:
468: 469: 470: 471: 472: 473: 474: 475: 476:
477: public function update($key, callable $updater, $default = null)
478: {
479: return $this->insert(
480: $key,
481: $updater(
482: Maybe::fromMaybe($default, $this->lookup($key))
483: )
484: );
485: }
486:
487: 488: 489: 490: 491: 492: 493: 494:
495: public function insert($key, $value)
496: {
497: Arguments::define($this->getKeyType(), $this->getValueType())
498: ->check($key, $value);
499:
500: $cloned = array_merge($this->value);
501:
502: $cloned[$key] = $value;
503:
504: return static::of($cloned);
505: }
506:
507: 508: 509:
510: public function values()
511: {
512: return new ArrayList(array_values($this->value));
513: }
514:
515: 516: 517:
518: public function entries()
519: {
520: return $this
521: ->map(function ($value, $key) {
522: return new ArrayList([$key, $value]);
523: })
524: ->toList();
525: }
526: }
527: