Arrays_Merge_Trait::mergeRecursiveCustom()   B
last analyzed

Complexity

Conditions 9
Paths 9

Size

Total Lines 55
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 55
rs 8.0555
c 0
b 0
f 0
cc 9
nc 9
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace JClaveau\Arrays;
3
4
use Traversable;
5
6
/**
7
 * Functions that deal with merging processes
8
 */
9
trait Arrays_Merge_Trait
10
{
11
12
    /**
13
     * Merges two rows by replacing their column values by MergeBuckets
14
     * containing their values.
15
     *
16
     * @param  array  $existing_row
17
     * @param  array  $conflict_row
18
     * @param  scalar $key
19
     *
20
     * @return array
21
     */
22
    public static function mergeInColumnBuckets(
23
        $existing_row,
24
        $conflict_row,
25
        $existing_key=null,
26
        $conflict_key=null
27
    ) {
28
        static::mustBeCountable($existing_row);
29
        static::mustBeCountable($conflict_row);
30
        
31
        $merged_row = [];
32
        foreach ($existing_row as $existing_column => $existing_value) {
33
            if ($existing_value instanceof MergeBucket) {
34
                $merged_row[ $existing_column ] = $existing_value;
35
            }
36
            else {
37
                if (isset($existing_key)) {
38
                    $merged_row[ $existing_column ] = MergeBucket::from([
39
                        $existing_key => $existing_value
40
                    ]);
41
                }
42
                else {
43
                    $merged_row[ $existing_column ] = MergeBucket::from([
44
                        $existing_value
45
                    ]);
46
                }
47
            }
48
        }
49
        
50
        foreach ($conflict_row as $conflict_column => $conflict_value) {
51
            if (! isset($merged_row[ $conflict_column ])) {
52
                $merged_row[ $conflict_column ] = new MergeBucket;
53
            }
54
            
55
            if ($conflict_value instanceof MergeBucket) {
56
                foreach ($conflict_value as $conflict_bucket_value) {
57
                    if (isset($conflict_key)) {
58
                        $merged_row[ $conflict_column ][$conflict_key] = $conflict_bucket_value;
59
                    }
60
                    else {
61
                        $merged_row[ $conflict_column ][] = $conflict_bucket_value;
62
                    }
63
                }
64
            }
65
            else {
66
                if (isset($conflict_key)) {
67
                    $merged_row[ $conflict_column ][$conflict_key] = $conflict_value;
68
                }
69
                else {
70
                    $merged_row[ $conflict_column ][] = $conflict_value;
71
                }
72
            }
73
        }
74
        
75
        return $merged_row;
76
    }
77
78
    /**
79
     * Merges two rows
80
     *
81
     * @param  array $existing_row
82
     * @param  array $conflict_row
83
     *
84
     * @return array
85
     * 
86
     * @deprecated
87
     */
88
    public static function mergePreservingDistincts(
89
        $existing_row,
90
        $conflict_row
91
    ) {
92
        return self::mergeInColumnBuckets($existing_row, $conflict_row);
93
    }
94
95
    /**
96
     * This is the cleaning last part of self::mergePreservingDistincts()
97
     *
98
     * @param  array|Countable   $row
0 ignored issues
show
Bug introduced by
The type JClaveau\Arrays\Countable was not found. Did you mean Countable? If so, make sure to prefix the type with \.
Loading history...
99
     * @param  array             $options : 'excluded_columns'
100
     *
101
     * @see mergePreservingDistincts()
102
     * @see cleanMergeDuplicates()
103
     */
104
    public static function cleanMergeBuckets($row, array $options=[])
105
    {
106
        static::mustBeCountable($row);
107
108
        $excluded_columns = isset($options['excluded_columns'])
109
                          ? $options['excluded_columns']
110
                          : []
111
                          ;
112
113
        foreach ($row as $column => &$values) {
114
            if (in_array($column, $excluded_columns)) {
115
                continue;
116
            }
117
118
            if ($values instanceof MergeBucket) {
119
                $values = $values->toArray();
120
            }
121
        }
122
123
        return $row;
124
    }
125
126
    /**
127
     * This is the cleaning part of self::mergePreservingDistincts()
128
     *
129
     * @param  array|Countable   $row
130
     * @param  array             $options : 'excluded_columns'
131
     */
132
    public static function cleanMergeDuplicates($row, array $options=[])
133
    {
134
        static::mustBeCountable($row);
135
136
        $excluded_columns = isset($options['excluded_columns'])
137
                          ? $options['excluded_columns']
138
                          : []
139
                          ;
140
141
        foreach ($row as $column => &$values) {
142
            if ( ! $values instanceof MergeBucket)
143
                continue;
144
145
            if (in_array($column, $excluded_columns))
146
                continue;
147
148
            $values = Arrays::unique($values);
149
            if (count($values) == 1)
150
                $values = $values[0];
151
        }
152
153
        return $row;
154
    }
155
156
    /**
157
     * Equivalent of array_merge_recursive with more options.
158
     *
159
     * @param array         $existing_row
160
     * @param array         $conflict_row
161
     * @param callable|null $merge_resolver
162
     * @param int           $max_depth
163
     *
164
     * + If exist only in conflict row => add
165
     * + If same continue
166
     * + If different merge as array
167
     */
168
    public static function mergeRecursiveCustom(
169
        $existing_row,
170
        $conflict_row,
171
        callable $merge_resolver=null,
172
        $max_depth=null
173
    ){
174
        static::mustBeCountable($existing_row);
175
        static::mustBeCountable($conflict_row);
176
177
        foreach ($conflict_row as $column => $conflict_value) {
178
179
            // not existing in first array
180
            if (!isset($existing_row[$column])) {
181
                $existing_row[$column] = $conflict_value;
182
                continue;
183
            }
184
185
            $existing_value = $existing_row[$column];
186
187
            // two arrays so we recurse
188
            if (is_array($existing_value) && is_array($conflict_value)) {
189
190
                if ($max_depth === null || $max_depth > 0) {
191
                    $existing_row[$column] = static::mergeRecursiveCustom(
192
                        $existing_value,
193
                        $conflict_value,
194
                        $merge_resolver,
195
                        $max_depth - 1
196
                    );
197
                    continue;
198
                }
199
            }
200
201
            if ($merge_resolver) {
202
                $existing_row[$column] = call_user_func_array(
203
                    $merge_resolver,
204
                    [
205
                        $existing_value,
206
                        $conflict_value,
207
                        $column,
208
                    ]
209
                );
210
            }
211
            else {
212
                // same resolution as array_merge_recursive
213
                if (!is_array($existing_value)) {
214
                    $existing_row[$column] = [$existing_value];
215
                }
216
217
                // We store the new value with their previous ones
218
                $existing_row[$column][] = $conflict_value;
219
            }
220
        }
221
222
        return $existing_row;
223
    }
224
225
    /**
226
     * Taken from Kohana's Arr class.
227
     *
228
     * Recursively merge two or more arrays. Values in an associative array
229
     * overwrite previous values with the same key. Values in an indexed array
230
     * are appended, but only when they do not already exist in the result.
231
     *
232
     * Note that this does not work the same as [array_merge_recursive](http://php.net/array_merge_recursive)!
233
     *
234
     *     $john = array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane'));
235
     *     $mary = array('name' => 'mary', 'children' => array('jane'));
236
     *
237
     *     // John and Mary are married, merge them together
238
     *     $john = Arr::merge($john, $mary);
239
     *
240
     *     // The output of $john will now be:
241
     *     array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane'))
242
     *
243
     * @param   array  $array1      initial array
244
     * @param   array  $array2,...  array to merge
245
     * @return  array
246
     */
247
    public static function merge($array1, $array2)
248
    {
249
        if (self::isAssociative($array2))
250
        {
251
            foreach ($array2 as $key => $value)
252
            {
253
                if (is_array($value)
254
                    AND isset($array1[$key])
255
                    AND is_array($array1[$key])
256
                )
257
                {
258
                    $array1[$key] = self::merge($array1[$key], $value);
259
                }
260
                else
261
                {
262
                    $array1[$key] = $value;
263
                }
264
            }
265
        }
266
        else
267
        {
268
            foreach ($array2 as $value)
269
            {
270
                if ( ! in_array($value, $array1, TRUE))
271
                {
272
                    $array1[] = $value;
273
                }
274
            }
275
        }
276
277
        if (func_num_args() > 2)
278
        {
279
            foreach (array_slice(func_get_args(), 2) as $array2)
0 ignored issues
show
introduced by
$array2 is overwriting one of the parameters of this function.
Loading history...
280
            {
281
                if (self::isAssociative($array2))
282
                {
283
                    foreach ($array2 as $key => $value)
284
                    {
285
                        if (is_array($value)
286
                            AND isset($array1[$key])
287
                            AND is_array($array1[$key])
288
                        )
289
                        {
290
                            $array1[$key] = self::merge($array1[$key], $value);
291
                        }
292
                        else
293
                        {
294
                            $array1[$key] = $value;
295
                        }
296
                    }
297
                }
298
                else
299
                {
300
                    foreach ($array2 as $value)
301
                    {
302
                        if ( ! in_array($value, $array1, TRUE))
303
                        {
304
                            $array1[] = $value;
305
                        }
306
                    }
307
                }
308
            }
309
        }
310
311
        return $array1;
312
    }
313
314
    /**
315
     * If an array contains merge buckets merge all those buckets with 
316
     * the other values.
317
     * 
318
     * This is a uni-dimensional flatten implementation
319
     * 
320
     * @param  array An array containing MergeBuckets
0 ignored issues
show
Bug introduced by
The type JClaveau\Arrays\An was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
321
     * @return array An array conatining all the values of the MergeBuckets
322
     *               and those of the initial array.
323
     */
324
    public static function flattenMergeBuckets(array $array)
325
    {
326
        $result = [];
327
        foreach ($array as $key => $value) {
328
            if (! $value instanceof MergeBucket) {
329
                $result[ $key ] = $value;
330
            }
331
            else {
332
                foreach ($value as $sub_key => $sub_value) {
333
                    if (is_int($sub_key)) {
334
                        $result[] = $sub_value;
335
                    }
336
                    elseif (isset($result[ $sub_key ])) {
337
                        throw new \LogicException(
338
                            "Conflict during flatten merge for key $sub_key between: \n"
339
                            ."Existing: " . var_export($result[ $sub_key ], true)
340
                            ."\n and \n"
341
                            ."Conflict: " . var_export($sub_value, true)
342
                        );
343
                    }
344
                    else {
345
                        $result[ $sub_key ] = $sub_value;
346
                    }
347
                }
348
            }
349
        }
350
351
        return $result;
352
    }
353
354
    /**/
355
}
356