1: <?php
2: /*
3: * Copyright (c) 2014 @trashtoy
4: * https://github.com/trashtoy/
5: *
6: * Permission is hereby granted, free of charge, to any person obtaining a copy of
7: * this software and associated documentation files (the "Software"), to deal in
8: * the Software without restriction, including without limitation the rights to use,
9: * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10: * Software, and to permit persons to whom the Software is furnished to do so,
11: * subject to the following conditions:
12: *
13: * The above copyright notice and this permission notice shall be included in all
14: * copies or substantial portions of the Software.
15: *
16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18: * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19: * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20: * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21: * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22: */
23: /**
24: * PHP class file.
25: * @auhtor trashtoy
26: * @since 2.0.0
27: */
28: namespace Peach\Util;
29:
30: /**
31: * 配列に関する操作を行うユーティリティクラスです.
32: */
33: class Arrays
34: {
35: /**
36: * このクラスはインスタンス化できません
37: */
38: private function __construct() {}
39:
40: /**
41: * {@link Arrays::max} と {@link Arrays::min} の共通部分の実装です.
42: * @param array $arr
43: * @param Comparator $c
44: * @param bool $isMax
45: * @return mixed
46: */
47: private static function getTop(array $arr, Comparator $c = null, $isMax = false)
48: {
49: if (!isset($c)) {
50: $c = DefaultComparator::getInstance();
51: }
52: $candidate = null;
53: foreach ($arr as $current) {
54: if (!isset($candidate)) {
55: $candidate = $current;
56: continue;
57: }
58: $comp = $c->compare($current, $candidate);
59: if (($isMax && 0 < $comp) || (!$isMax && $comp < 0)) {
60: $candidate = $current;
61: }
62: }
63: return $candidate;
64: }
65:
66: /**
67: * 指定された配列の各要素の中で「最も大きい」値を返します.
68: *
69: * 配列に含まれるオブジェクトが {@link Comparable} を実装している場合は
70: * そのオブジェクトの {@link Comparable::compareTo}
71: * メソッドを使って大小の比較を行います. それ以外の値の場合は
72: * {@link http://jp.php.net/manual/ja/language.operators.comparison.php PHP の比較演算子のルール}
73: * に従って大小比較が行われます.
74: *
75: * 大小の比較方法が型によって異なるため,
76: * 配列内の要素の型に一貫性がない場合は, 格納順序の違いによって
77: * 異なる結果となる可能性があります.
78: * 意図しない動作を防ぐためには, あらかじめ {@link Arrays::pickup}
79: * を使って配列に含まれる型を揃えてください.
80: *
81: * オプションとして第 2 引数に {@link Comparator}
82: * オブジェクトを指定することもできます.
83: * もし第 2 引数が指定された場合は {@link Comparator::compare}
84: * メソッドを使って大小比較を行います.
85: *
86: * @param array $arr
87: * @param Comparator $c
88: * @return mixed 引数 $arr の中で最も大きな値. 配列が空の場合は NULL
89: */
90: public static function max(array $arr, Comparator $c = null)
91: {
92: return self::getTop($arr, $c, true);
93: }
94:
95: /**
96: * 指定された配列の各要素の中で「最も小さい」値を返します.
97: *
98: * 配列に含まれるオブジェクトが {@link Comparable} を実装している場合は
99: * そのオブジェクトの {@link Comparable::compareTo}
100: * メソッドを使って大小の比較を行います. それ以外の値の場合は
101: * {@link http://jp.php.net/manual/ja/language.operators.comparison.php PHP の比較演算子のルール}
102: * に従って大小比較が行われます.
103: *
104: * 大小の比較方法が型によって異なるため,
105: * 配列内の要素の型に一貫性がない場合は, 格納順序の違いによって
106: * 異なる結果となる可能性があります.
107: * 意図しない動作を防ぐためには, あらかじめ {@link Arrays::pickup}
108: * を使って配列に含まれる型を揃えてください.
109: *
110: * オプションとして第 2 引数に {@link Comparator}
111: * オブジェクトを指定することもできます.
112: * もし第 2 引数が指定された場合は {@link Comparator::compare}
113: * メソッドを使って大小比較を行います.
114: *
115: * @param array $arr
116: * @param Comparator $c
117: * @return mixed $arr の中で最も大きな値. 配列が空の場合は NULL
118: */
119: public static function min(array $arr, Comparator $c = null)
120: {
121: return self::getTop($arr, $c, false);
122: }
123:
124: /**
125: * 配列の中から, $type で指定した型の値だけを取り出します.
126: * $keyFlag に TRUE を指定した場合はキーと要素の関連付けを維持します.
127: * $type に指定できる文字列は以下のとおりです. (大文字・小文字は区別しません)
128: *
129: * - int
130: * - integer
131: * - numeric
132: * - float (※ numeric と同じです. int 型の値もマッチングします.)
133: * - string
134: * - bool
135: * - boolean
136: * - array
137: * - object
138: * - resource
139: * - null
140: *
141: * 上記に挙げた以外の文字列を指定した場合は, クラス (インタフェース) 名として扱います.
142: *
143: * @param array $arr 対象の配列
144: * @param string $type 'int', 'integer', 'numeric', 'float', 'string', 'bool',
145: * 'object', 'resource' など.
146: * それ以外の文字列はクラス (インタフェース) 名として扱う
147: * @param bool $keyFlag 関連付けを維持する場合は TRUE (デフォルトは FALSE)
148: * @return array
149: */
150: public static function pickup(array $arr, $type, $keyFlag = false)
151: {
152: $result = array();
153: foreach ($arr as $key => $value) {
154: if (self::pickupMatch($value, $type)) {
155: if ($keyFlag) {
156: $result[$key] = $value;
157: } else {
158: $result[] = $value;
159: }
160: }
161: }
162: return $result;
163: }
164:
165: /**
166: * 指定された値が第 2 引数で指定した型にマッチするかどうかを調べます.
167: *
168: * @param string $value 検査対象の値
169: * @param string $type 型
170: * @return bool 引数 $value の型が $type に合致する場合に true
171: */
172: private static function pickupMatch($value, $type)
173: {
174: $ltype = strtolower($type);
175: switch ($ltype) {
176: case 'int':
177: case 'integer':
178: return is_int($value);
179: case 'float':
180: case 'numeric':
181: return is_numeric($value);
182: case 'string':
183: return is_string($value);
184: case 'null':
185: return is_null($value);
186: case 'bool':
187: case 'boolean':
188: return is_bool($value);
189: case 'array':
190: return is_array($value);
191: case 'resource':
192: return is_resource($value);
193: case 'object':
194: return is_object($value);
195: default:
196: return is_object($value) && ($value instanceof $type);
197: }
198: }
199:
200: /**
201: * 指定された配列をソートします.
202: * 配列のキーは連番で初期化されます.
203: * コンパレータが指定されなかった場合は {@link DefaultComparator} が適用されます.
204: *
205: * @param array $arr ソート対象の配列
206: * @param Comparator $c コンパレータ
207: * @return array
208: */
209: public static function sort(array $arr, Comparator $c = null)
210: {
211: if (count($arr) < 2) {
212: return $arr;
213: }
214: if (!isset($c)) {
215: $c = DefaultComparator::getInstance();
216: }
217: $func = function ($var1, $var2) use ($c) {
218: return $c->compare($var1, $var2);
219: };
220: usort($arr, $func);
221: return $arr;
222: }
223:
224: /**
225: * 配列のキーと値のマッピングを保持しながら, 指定された配列をソートします.
226: * コンパレータが指定されなかった場合は {@link DefaultComparator} が適用されます.
227: *
228: * @param array $arr ソート対象の配列
229: * @param Comparator $c コンパレータ
230: * @return array ソート後の配列
231: */
232: public static function asort(array $arr, Comparator $c = null)
233: {
234: if (count($arr) < 2) {
235: return $arr;
236: }
237: if (!isset($c)) {
238: $c = DefaultComparator::getInstance();
239: }
240: $func = function ($var1, $var2) use ($c) {
241: return $c->compare($var1, $var2);
242: };
243: uasort($arr, $func);
244: return $arr;
245: }
246:
247: /**
248: * 引数の配列または値を連結して, 一つの配列として返します.
249: *
250: * 例えば
251: *
252: * <code>
253: * $arr1 = array(10, 20, 30);
254: * $arr2 = array(40, 50);
255: * $concat = Arrays::concat($arr1, "X", $arr2, "Y");
256: * </code>
257: *
258: * の結果は
259: * <code>
260: * array(10, 20, 30, "X", 40, 50, "Y")
261: * </code>
262: * に等しくなります。
263: *
264: * この関数は多次元配列はサポートしません.
265: *
266: * <code>
267: * $arr1 = array("hoge", "fuga");
268: * $arr2 = array(
269: * array(1, 2, 3),
270: * array(5, 7)
271: * );
272: * $concat = Arrays::concat($arr1, $arr2);
273: * </code>
274: *
275: * の結果は
276: *
277: * <code>
278: * array(
279: * "hoge",
280: * "fuga",
281: * array(1, 2, 3),
282: * array(5, 7)
283: * )
284: * </code>
285: *
286: * となります.
287: *
288: * この関数は, 配列に含まれるキーをすべて無視します.
289: * 結果の配列のキーは連番で初期化されます.
290: *
291: * @return array
292: */
293: public static function concat()
294: {
295: $args = func_get_args();
296: $result = array();
297: foreach ($args as $arg) {
298: if (is_array($arg)) {
299: foreach ($arg as $subarg) {
300: $result[] = $subarg;
301: }
302: } else {
303: $result[] = $arg;
304: }
305: }
306: return $result;
307: }
308:
309: /**
310: * 指定された配列から, 重複した値を取り除いた結果を返します.
311: * 重複かどうかの判定は, 引数に指定されたコンパレータを使って行われます.
312: * コンパレータが指定されなかった場合は {@link DefaultComparator} が適用されます.
313: *
314: * @param array $arr
315: * @param Comparator $c コンパレータ
316: * @return array
317: */
318: public static function unique(array $arr, Comparator $c = null)
319: {
320: if (!isset($c)) {
321: $c = DefaultComparator::getInstance();
322: }
323: $sorted = self::asort($arr, $c);
324: $delKey = array();
325: list($lastKey, $lastValue) = each($sorted);
326: while (list($key, $value) = each($sorted)) {
327: if ($c->compare($value, $lastValue) === 0 && $value == $lastValue) {
328: $delKey[] = $key;
329: continue;
330: } else {
331: $lastKey = $key;
332: $lastValue = $value;
333: }
334: }
335: foreach ($delKey as $key) {
336: unset($arr[$key]);
337: }
338: return $arr;
339: }
340: }
341: