Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
12 / 12
CRAP
100.00% covered (success)
100.00%
1 / 1
ArrayOperation
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
12 / 12
45
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 set
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getKeys
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValues
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getElements
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 getFirst
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
9
 getLast
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
9
 filterByElement
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
 filterByKey
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 filterByValue
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 filter
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3/**
4 * @license MIT
5 * @author hazuki3417<hazuki3417@gmail.com>
6 * @copyright 2024 hazuki3417 all rights reserved.
7 */
8
9namespace Selen\Array;
10
11use ArrayIterator;
12
13/**
14 * 配列の操作を提供するクラスです。
15 */
16class ArrayOperation
17{
18    public const SEARCH_CONDITION_KEY     = 'key';
19    public const SEARCH_CONDITION_VALUE   = 'value';
20    public const SEARCH_CONDITION_ELEMENT = 'element';
21    public const RETURN_CONDITION_KEY     = 'key';
22    public const RETURN_CONDITION_VALUE   = 'value';
23    public const RETURN_CONDITION_ELEMENT = 'element';
24
25    /** @var ArrayIterator<int|string,mixed> イテレータクラスのインスタンス */
26    private ArrayIterator $arrayIterator;
27
28    /**
29     * 新しいArrayOperationインスタンスを生成します。
30     *
31     * @param ArrayIterator<int|string,mixed> $arrayIterator イテレータクラスのインスタンスを渡します
32     */
33    public function __construct(ArrayIterator $arrayIterator)
34    {
35        $this->arrayIterator = $arrayIterator;
36    }
37
38    /**
39     * 新しいArrayOperationインスタンスを生成します。
40     *
41     * @param array<mixed,mixed> $value 配列を渡します
42     */
43    public static function set(array $value): ArrayOperation
44    {
45        return new self(new ArrayIterator($value));
46    }
47
48    /**
49     * 配列のキーを取得します
50     *
51     * @return array<int,mixed> 配列のキーを返します
52     */
53    public function getKeys(): array
54    {
55        return array_keys($this->arrayIterator->getArrayCopy());
56    }
57
58    /**
59     * 配列の値を取得します
60     *
61     * @return array<int,mixed> 配列の値を返します
62     */
63    public function getValues(): array
64    {
65        return array_values($this->arrayIterator->getArrayCopy());
66    }
67
68    /**
69     * 配列の要素を取得します
70     *
71     * @return array<mixed,mixed> 配列の要素を返します
72     */
73    public function getElements(): array
74    {
75        return $this->arrayIterator->getArrayCopy();
76    }
77
78    /**
79     * 指定した条件に一致する要素を取得します
80     *
81     * @param string $returnCondition 取得条件を指定します
82     *
83     * @return array<mixed,mixed> 配列を返します
84     */
85    public function get(string $returnCondition = self::RETURN_CONDITION_ELEMENT): array
86    {
87        switch ($returnCondition) {
88            case self::RETURN_CONDITION_KEY:
89                return $this->getKeys();
90            case self::RETURN_CONDITION_VALUE:
91                return $this->getValues();
92            case self::RETURN_CONDITION_ELEMENT:
93                return $this->getElements();
94            default:
95                break;
96        }
97        throw new \InvalidArgumentException('invalid return condition.');
98    }
99
100    /**
101     * 配列の先頭要素を取得します
102     *
103     * @param string $searchCondition 検索条件を指定します
104     *
105     * @return array<mixed,mixed> 配列を返します
106     */
107    public function getFirst(string $searchCondition = self::SEARCH_CONDITION_ELEMENT): array
108    {
109        $isEmpty = $this->arrayIterator->count() === 0;
110
111        if (!$isEmpty) {
112            $this->arrayIterator->rewind();
113        }
114
115        switch ($searchCondition) {
116            case self::SEARCH_CONDITION_KEY:
117                return $isEmpty ? [] : [$this->arrayIterator->key()];
118            case self::SEARCH_CONDITION_VALUE:
119                return $isEmpty ? [] : [$this->arrayIterator->current()];
120            case self::SEARCH_CONDITION_ELEMENT:
121                return $isEmpty ? [] : [$this->arrayIterator->key() => $this->arrayIterator->current()];
122            default:
123                break;
124        }
125        throw new \InvalidArgumentException('invalid search condition.');
126    }
127
128    /**
129     * 配列の末尾要素を取得します
130     *
131     * @param string $searchCondition 検索条件を指定します
132     *
133     * @return array<mixed,mixed> 配列を返します
134     */
135    public function getLast(string $searchCondition = self::SEARCH_CONDITION_ELEMENT): array
136    {
137        $isEmpty = $this->arrayIterator->count() === 0;
138
139        if (!$isEmpty) {
140            $lastIndex = $this->arrayIterator->count() - 1;
141            $this->arrayIterator->seek($lastIndex);
142        }
143
144        switch ($searchCondition) {
145            case self::SEARCH_CONDITION_KEY:
146                return $isEmpty ? [] : [$this->arrayIterator->key()];
147            case self::SEARCH_CONDITION_VALUE:
148                return $isEmpty ? [] : [$this->arrayIterator->current()];
149            case self::SEARCH_CONDITION_ELEMENT:
150                return $isEmpty ? [] : [$this->arrayIterator->key() => $this->arrayIterator->current()];
151            default:
152                break;
153        }
154        throw new \InvalidArgumentException('invalid search condition.');
155    }
156
157    /**
158     * 指定した要素に一致する要素を抽出します
159     *
160     * @param array<int,array<string|int,mixed>> $elements 検索対象の要素を指定します
161     */
162    public function filterByElement(array $elements): ArrayOperation
163    {
164        for ($this->arrayIterator->rewind(); $this->arrayIterator->valid();) {
165            $isElementMatch = false;
166
167            foreach ($elements as $elementKey => $elementValue) {
168                $isKeyMatch   = $elementKey   === $this->arrayIterator->key();
169                $isValueMatch = $elementValue === $this->arrayIterator->current();
170
171                if ($isKeyMatch && $isValueMatch) {
172                    $isElementMatch = true;
173                    break;
174                }
175            }
176
177            $isElementMatch ?
178                $this->arrayIterator->next() :
179                $this->arrayIterator->offsetUnset($this->arrayIterator->key());
180        }
181        return $this;
182    }
183
184    /**
185     * 指定したkeyに一致する要素を抽出します
186     *
187     * @param string|int $key 検索対象のkeyを指定します
188     */
189    public function filterByKey(string|int ...$key): ArrayOperation
190    {
191        for ($this->arrayIterator->rewind(); $this->arrayIterator->valid();) {
192            in_array($this->arrayIterator->key(), $key, true) ?
193                $this->arrayIterator->next() :
194                $this->arrayIterator->offsetUnset($this->arrayIterator->key());
195        }
196        return $this;
197    }
198
199    /**
200     * 指定したvalueに一致する要素を抽出します
201     *
202     * @param mixed $value 検索対象のvalueを指定します
203     */
204    public function filterByValue(mixed ...$value): ArrayOperation
205    {
206        for ($this->arrayIterator->rewind(); $this->arrayIterator->valid();) {
207            in_array($this->arrayIterator->current(), $value, true) ?
208                $this->arrayIterator->next() :
209                $this->arrayIterator->offsetUnset($this->arrayIterator->key());
210        }
211        return $this;
212    }
213
214    /**
215     * 指定した条件に一致する要素を抽出します
216     *
217     * @param array<mixed,mixed> $needle          検索対象の要素を指定します
218     * @param string             $searchCondition 検索条件を指定します
219     */
220    public function filter(array $needle, string $searchCondition = self::SEARCH_CONDITION_ELEMENT): ArrayOperation
221    {
222        switch ($searchCondition) {
223            case self::SEARCH_CONDITION_KEY:
224                return $this->filterByKey(...$needle);
225            case self::SEARCH_CONDITION_VALUE:
226                return $this->filterByValue(...$needle);
227            case self::SEARCH_CONDITION_ELEMENT:
228                return $this->filterByElement($needle);
229            default:
230                break;
231        }
232        throw new \InvalidArgumentException('invalid search condition.');
233    }
234}