Passed
Push — master ( 258911...b0482b )
by Nick
01:48
created

AssociativeArray::offsetGet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * Associative array class.
5
 *
6
 * @author  Nick Lai <[email protected]>
7
 * @license https://opensource.org/licenses/mit-license.php MIT
8
 * @link    https://github.com/nick-lai/associative-array
9
 */
10
11
namespace NickLai;
12
13
use ArrayAccess;
14
use ArrayIterator;
15
use Countable;
16
use IteratorAggregate;
17
use Traversable;
18
19
class AssociativeArray implements ArrayAccess, Countable, IteratorAggregate
20
{
21
    /**
22
     * The rows contained in the associative array.
23
     *
24
     * @var array
25
     */
26
    protected $rows = [];
27
28
    /**
29
     * Create a new associative array.
30
     *
31
     * @param mixed $rows
32
     * @return void
33
     */
34
    public function __construct($rows = [])
35
    {
36
        $this->rows = $this->getAssociativeRows($rows);
37
    }
38
39
    /**
40
     * Create a new associative array instance.
41
     *
42
     * @param mixed $rows
43
     * @return static
44
     */
45
    public static function make($rows = [])
46
    {
47
        return new static($rows);
48
    }
49
50
    /**
51
     * Get rows of selected columns.
52
     *
53
     * @param string|array $keys
54
     * @return static
55
     */
56
    public function select($keys)
57
    {
58
        if (!is_array($keys)) {
59
            $keys = (array)$keys;
60
        }
61
62
        $keys = array_flip($keys);
63
64
        return new static(array_map(function($row) use ($keys) {
65
            return array_intersect_key($row, $keys);
66
        }, $this->rows));
67
    }
68
69
    /**
70
     * Filter the rows using the given callback.
71
     *
72
     * @param callable $callback
73
     * @return static
74
     */
75
    public function where(callable $callback)
76
    {
77
        return new static(array_filter($this->rows, $callback, ARRAY_FILTER_USE_BOTH));
78
    }
79
80
    /**
81
     * Inner join rows
82
     *
83
     * @param array $rows
84
     * @param callable $on
85
     * @return static
86
     */
87
    public function innerJoin($rows, callable $on)
88
    {
89
        $result = [];
90
91
        foreach ($this->rows as $leftRow) {
92
            foreach ($rows as $rightRow) {
93
                if ($on($leftRow, $rightRow)) {
94
                    $result[] = $leftRow + $rightRow;
95
                    break;
96
                }
97
            }
98
        }
99
100
        return new static($result);
101
    }
102
103
    /**
104
     * Left join rows
105
     *
106
     * @param array $rows
107
     * @param callable $on
108
     * @return static
109
     */
110
    public function leftJoin($rows, callable $on)
111
    {
112
        $nullRightRow = [];
113
114
        foreach ((new static($rows))->first() as $key => $value) {
115
            $nullRightRow[$key] = null;
116
        }
117
118
        $result = [];
119
120
        foreach ($this->rows as $leftRow) {
121
            $row = $leftRow + $nullRightRow;
122
            foreach ($rows as $rightRow) {
123
                if ($on($leftRow, $rightRow)) {
124
                    $row = $leftRow + $rightRow;
125
                    break;
126
                }
127
            }
128
            $result[] = $row;
129
        }
130
131
        return new static($result);
132
    }
133
134
    /**
135
     * Right join rows
136
     *
137
     * @param array $rows
138
     * @param callable $on
139
     * @return static
140
     */
141
    public function rightJoin($rows, callable $on)
142
    {
143
        return (new static($rows))->leftJoin($this->rows, $on);
144
    }
145
146
    /**
147
     * Order by keys
148
     *
149
     * @param string|array $keys
150
     * @param string|array $directions
151
     * @return static
152
     */
153
    public function orderBy($keys, $directions = 'asc')
154
    {
155
        if (!is_array($keys)) {
156
            $keys = (array)$keys;
157
        }
158
159
        $i = 0;
160
        $key2Direction = [];
161
        $isStringDirections = is_string($directions);
162
163
        foreach ($keys as $key) {
164
            $key2Direction[$key] = $isStringDirections ? $directions : ($directions[$i] ?? 'asc');
165
            $i++;
166
        }
167
168
        $result = $this->rows;
169
170
        usort($result, function($a, $b) use ($keys, $key2Direction) {
171
            foreach ($keys as $key) {
172
                if ($cmpVal = $key2Direction[$key] === 'desc'
173
                        ? $b[$key] <=> $a[$key]
174
                        : $a[$key] <=> $b[$key]) {
175
                    return $cmpVal;
176
                }
177
            }
178
            return 0;
179
        });
180
181
        return new static($result);
182
    }
183
184
    /**
185
     * Groups an associative array by keys.
186
     *
187
     * @param array|string $keys
188
     * @return static
189
     */
190
    public function groupBy($keys)
191
    {
192
        if (!is_array($keys)) {
193
            $keys = (array)$keys;
194
        }
195
196
        $result = [];
197
198
        foreach ($this->rows as $row) {
199
            $groupKey = implode(',', array_intersect_key($row, array_flip($keys)));
200
            if (!isset($result[$groupKey])) {
201
                $result[$groupKey] = $row;
202
            }
203
        }
204
205
        return new static(array_values($result));
206
    }
207
208
    /**
209
     * Return the first row
210
     *
211
     * @param mixed $default
212
     * @return mixed
213
     */
214
    public function first($default = null)
215
    {
216
        foreach ($this->rows as $row) {
217
            return $row;
218
        }
219
220
        return $default;
221
    }
222
223
    /**
224
     * Return the last row
225
     *
226
     * @param mixed $default
227
     * @return mixed
228
     */
229
    public function last($default = null)
230
    {
231
        foreach (array_reverse($this->rows) as $row) {
232
            return $row;
233
        }
234
235
        return $default;
236
    }
237
238
    /**
239
     * Count the number of rows in the associative array.
240
     *
241
     * @return int
242
     */
243
    public function count()
244
    {
245
        return count($this->rows);
246
    }
247
248
    /**
249
     * Get the sum of a given key.
250
     *
251
     * @param string $key
252
     * @return mixed
253
     */
254
    public function sum($key)
255
    {
256
        return array_sum(array_column($this->rows, $key));
257
    }
258
259
    /**
260
     * Get the average value of a given key.
261
     *
262
     * @param string $key
263
     * @return mixed
264
     */
265
    public function avg($key)
266
    {
267
        $sum = $this->sum($key);
268
        return $sum ? ($sum / $this->count()) : $sum;
269
    }
270
271
    /**
272
     * Get the instance as an array.
273
     *
274
     * @return array
275
     */
276
    public function toArray()
277
    {
278
        return array_map(function($row) {
279
            return $row instanceof self ? $row->toArray() : $row;
280
        }, $this->rows);
281
    }
282
283
    /**
284
     * Get an iterator for the rows.
285
     *
286
     * @return \ArrayIterator
287
     */
288
    public function getIterator()
289
    {
290
        return new ArrayIterator($this->rows);
291
    }
292
293
    /**
294
     * Determine if a row exists at an offset.
295
     *
296
     * @param mixed $offset
297
     * @return bool
298
     */
299
    public function offsetExists($offset)
300
    {
301
        return array_key_exists($offset, $this->rows);
302
    }
303
304
    /**
305
     * Get a row at a given offset.
306
     *
307
     * @param mixed $offset
308
     * @return mixed
309
     */
310
    public function offsetGet($offset)
311
    {
312
        return $this->rows[$offset];
313
    }
314
315
    /**
316
     * Set the row at a given offset.
317
     *
318
     * @param mixed $offset
319
     * @param mixed $row
320
     * @return void
321
     */
322
    public function offsetSet($offset, $row)
323
    {
324
        if (is_null($offset)) {
325
            $this->rows[] = $row;
326
        } else {
327
            $this->rows[$offset] = $row;
328
        }
329
    }
330
331
    /**
332
     * Unset the row at a given offset.
333
     *
334
     * @param string $offset
335
     * @return void
336
     */
337
    public function offsetUnset($offset)
338
    {
339
        unset($this->rows[$offset]);
340
    }
341
342
    /**
343
     * Results array of rows from associative array or traversable.
344
     *
345
     * @param mixed $rows
346
     * @return array
347
     */
348
    protected function getAssociativeRows($rows)
349
    {
350
        if (is_array($rows)) {
351
            return $rows;
352
        } elseif ($rows instanceof self) {
353
            return $rows->toArray();
354
        } elseif ($rows instanceof Traversable) {
355
            return iterator_to_array($rows);
356
        }
357
358
        return (array)$rows;
359
    }
360
}
361