Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
4
 
5
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
6
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
7
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
8
 
9
class Unique
10
{
11
    /**
12
     * UNIQUE
13
     * The UNIQUE function searches for value either from a one-row or one-column range or from an array.
14
     *
15
     * @param mixed $lookupVector The range of cells being searched
16
     * @param mixed $byColumn Whether the uniqueness should be determined by row (the default) or by column
17
     * @param mixed $exactlyOnce Whether the function should return only entries that occur just once in the list
18
     *
19
     * @return mixed The unique values from the search range
20
     */
21
    public static function unique(mixed $lookupVector, mixed $byColumn = false, mixed $exactlyOnce = false): mixed
22
    {
23
        if (!is_array($lookupVector)) {
24
            // Scalars are always returned "as is"
25
            return $lookupVector;
26
        }
27
 
28
        $byColumn = (bool) $byColumn;
29
        $exactlyOnce = (bool) $exactlyOnce;
30
 
31
        return ($byColumn === true)
32
            ? self::uniqueByColumn($lookupVector, $exactlyOnce)
33
            : self::uniqueByRow($lookupVector, $exactlyOnce);
34
    }
35
 
36
    private static function uniqueByRow(array $lookupVector, bool $exactlyOnce): mixed
37
    {
38
        // When not $byColumn, we count whole rows or values, not individual values
39
        //      so implode each row into a single string value
40
        array_walk(
41
            $lookupVector,
42
            function (array &$value): void {
43
                $valuex = '';
44
                $separator = '';
45
                $numericIndicator = "\x01";
46
                foreach ($value as $cellValue) {
47
                    $valuex .= $separator . $cellValue;
48
                    $separator = "\x00";
49
                    if (is_int($cellValue) || is_float($cellValue)) {
50
                        $valuex .= $numericIndicator;
51
                    }
52
                }
53
                $value = $valuex;
54
            }
55
        );
56
 
57
        $result = self::countValuesCaseInsensitive($lookupVector);
58
 
59
        if ($exactlyOnce === true) {
60
            $result = self::exactlyOnceFilter($result);
61
        }
62
 
63
        if (count($result) === 0) {
64
            return ExcelError::CALC();
65
        }
66
 
67
        $result = array_keys($result);
68
 
69
        // restore rows from their strings
70
        array_walk(
71
            $result,
72
            function (string &$value): void {
73
                $value = explode("\x00", $value);
74
                foreach ($value as &$stringValue) {
75
                    if (str_ends_with($stringValue, "\x01")) {
76
                        // x01 should only end a string which is otherwise a float or int,
77
                        // so phpstan is technically correct but what it fears should not happen.
78
                        $stringValue = 0 + substr($stringValue, 0, -1); //@phpstan-ignore-line
79
                    }
80
                }
81
            }
82
        );
83
 
84
        return (count($result) === 1) ? array_pop($result) : $result;
85
    }
86
 
87
    private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce): mixed
88
    {
89
        $flattenedLookupVector = Functions::flattenArray($lookupVector);
90
 
91
        if (count($lookupVector, COUNT_RECURSIVE) > count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
92
            // We're looking at a full column check (multiple rows)
93
            $transpose = Matrix::transpose($lookupVector);
94
            $result = self::uniqueByRow($transpose, $exactlyOnce);
95
 
96
            return (is_array($result)) ? Matrix::transpose($result) : $result;
97
        }
98
 
99
        $result = self::countValuesCaseInsensitive($flattenedLookupVector);
100
 
101
        if ($exactlyOnce === true) {
102
            $result = self::exactlyOnceFilter($result);
103
        }
104
 
105
        if (count($result) === 0) {
106
            return ExcelError::CALC();
107
        }
108
 
109
        $result = array_keys($result);
110
 
111
        return $result;
112
    }
113
 
114
    private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
115
    {
116
        $caseInsensitiveCounts = array_count_values(
117
            array_map(
118
                fn (string $value): string => StringHelper::strToUpper($value),
119
                $caseSensitiveLookupValues
120
            )
121
        );
122
 
123
        $caseSensitiveCounts = [];
124
        foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
125
            if (is_numeric($caseInsensitiveKey)) {
126
                $caseSensitiveCounts[$caseInsensitiveKey] = $count;
127
            } else {
128
                foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
129
                    if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
130
                        $caseSensitiveCounts[$caseSensitiveValue] = $count;
131
 
132
                        break;
133
                    }
134
                }
135
            }
136
        }
137
 
138
        return $caseSensitiveCounts;
139
    }
140
 
141
    private static function exactlyOnceFilter(array $values): array
142
    {
143
        return array_filter(
144
            $values,
145
            fn ($value): bool => $value === 1
146
        );
147
    }
148
}