PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Calculation / LookupRef / Unique.php

Unique.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Calculation/LookupRef/Unique.php

167 lines 4.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
4
5 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Functions;
6 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
7 use TablePress\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($lookupVector, $byColumn = false, $exactlyOnce = false)
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 /** @param mixed[] $lookupVector
37 * @return mixed */
38 private static function uniqueByRow(array $lookupVector, bool $exactlyOnce)
39 {
40 // When not $byColumn, we count whole rows or values, not individual values
41 // so implode each row into a single string value
42 array_walk(
43 $lookupVector,
44 // @phpstan-ignore argument.type (not sure what is needed)
45 function (array &$value): void {
46 $valuex = '';
47 $separator = '';
48 $numericIndicator = "\x01";
49 foreach ($value as $cellValue) {
50 /** @var scalar $cellValue */
51 $valuex .= $separator . $cellValue;
52 $separator = "\x00";
53 if (is_int($cellValue) || is_float($cellValue)) {
54 $valuex .= $numericIndicator;
55 }
56 }
57 $value = $valuex;
58 }
59 );
60
61 /** @var string[] $lookupVector */
62 $result = self::countValuesCaseInsensitive($lookupVector);
63
64 if ($exactlyOnce === true) {
65 $result = self::exactlyOnceFilter($result);
66 }
67
68 if (count($result) === 0) {
69 return ExcelError::CALC();
70 }
71
72 $result = array_keys($result);
73
74 // restore rows from their strings
75 array_walk(
76 $result,
77 function (string &$value): void {
78 $value = explode("\x00", $value);
79 foreach ($value as &$stringValue) {
80 if (str_ends_with($stringValue, "\x01")) {
81 // x01 should only end a string which is otherwise a float or int,
82 // so phpstan is technically correct but what it fears should not happen.
83 $stringValue = 0 + substr($stringValue, 0, -1); //@phpstan-ignore binaryOp.invalid (stringValue might not be numeric?)
84 }
85 }
86 }
87 );
88
89 return (count($result) === 1) ? array_pop($result) : $result;
90 }
91
92 /** @param mixed[] $lookupVector
93 * @return mixed */
94 private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce)
95 {
96 /** @var string[] */
97 $flattenedLookupVector = Functions::flattenArray($lookupVector);
98
99 if (count($lookupVector, COUNT_RECURSIVE) > count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
100 // We're looking at a full column check (multiple rows)
101 $transpose = Matrix::transpose($lookupVector);
102 $result = self::uniqueByRow($transpose, $exactlyOnce);
103
104 return (is_array($result)) ? Matrix::transpose($result) : $result;
105 }
106
107 $result = self::countValuesCaseInsensitive($flattenedLookupVector);
108
109 if ($exactlyOnce === true) {
110 $result = self::exactlyOnceFilter($result);
111 }
112
113 if (count($result) === 0) {
114 return ExcelError::CALC();
115 }
116
117 $result = array_keys($result);
118
119 return $result;
120 }
121
122 /**
123 * @param string[] $caseSensitiveLookupValues
124 *
125 * @return mixed[]
126 */
127 private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
128 {
129 $caseInsensitiveCounts = array_count_values(
130 array_map(
131 fn (string $value): string => StringHelper::strToUpper($value),
132 $caseSensitiveLookupValues
133 )
134 );
135
136 $caseSensitiveCounts = [];
137 foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
138 if (is_numeric($caseInsensitiveKey)) {
139 $caseSensitiveCounts[$caseInsensitiveKey] = $count;
140 } else {
141 foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
142 if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
143 $caseSensitiveCounts[$caseSensitiveValue] = $count;
144
145 break;
146 }
147 }
148 }
149 }
150
151 return $caseSensitiveCounts;
152 }
153
154 /**
155 * @param mixed[] $values
156 *
157 * @return mixed[]
158 */
159 private static function exactlyOnceFilter(array $values): array
160 {
161 return array_filter(
162 $values,
163 fn ($value): bool => $value === 1
164 );
165 }
166 }
167