Passed
Pull Request — master (#4144)
by Owen
15:51
created

VLookup::numeric()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
ccs 0
cts 0
cp 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
8
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
9
10
class VLookup extends LookupBase
11
{
12
    use ArrayEnabled;
13
14
    /**
15
     * VLOOKUP
16
     * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
17
     *     in the same row based on the index_number.
18
     *
19
     * @param mixed $lookupValue The value that you want to match in lookup_array
20
     * @param mixed $lookupArray The range of cells being searched
21
     * @param mixed $indexNumber The column number in table_array from which the matching value must be returned.
22
     *                                The first column is 1.
23
     * @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
24
     *
25
     * @return mixed The value of the found cell
26
     */
27 35
    public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
28
    {
29 35
        if (is_array($lookupValue) || is_array($indexNumber)) {
30 21
            return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
31
        }
32
33 35
        $notExactMatch = (bool) ($notExactMatch ?? true);
34
35
        try {
36 35
            self::validateLookupArray($lookupArray);
37 35
            $indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
38 3
        } catch (Exception $e) {
39 3
            return $e->getMessage();
40
        }
41
42 32
        $f = array_keys($lookupArray);
43 32
        $firstRow = array_pop($f);
44 32
        if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
45 3
            return ExcelError::REF();
46
        }
47 29
        $columnKeys = array_keys($lookupArray[$firstRow]);
48 29
        $returnColumn = $columnKeys[--$indexNumber];
49 29
        $firstColumn = array_shift($columnKeys) ?? 1;
50
51 29
        if (!$notExactMatch) {
52
            /** @var callable $callable */
53 9
            $callable = [self::class, 'vlookupSort'];
54 9
            uasort($lookupArray, $callable);
55
        }
56
57 29
        $rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
58
59 29
        if ($rowNumber !== null) {
60
            // return the appropriate value
61 23
            return $lookupArray[$rowNumber][$returnColumn];
62
        }
63
64 6
        return ExcelError::NA();
65
    }
66
67 9
    private static function vlookupSort(array $a, array $b): int
68
    {
69 9
        reset($a);
70 9
        $firstColumn = key($a);
71 9
        $aLower = StringHelper::strToLower((string) $a[$firstColumn]);
72 9
        $bLower = StringHelper::strToLower((string) $b[$firstColumn]);
73
74 9
        if ($aLower == $bLower) {
75 1
            return 0;
76
        }
77
78 9
        return ($aLower < $bLower) ? -1 : 1;
79
    }
80
81
    /**
82
     * @param mixed $lookupValue The value that you want to match in lookup_array
83
     * @param  int|string $column
84
     */
85 29
    private static function vLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
86
    {
87 29
        $lookupLower = StringHelper::strToLower((string) $lookupValue);
88
89 29
        $rowNumber = null;
90 29
        foreach ($lookupArray as $rowKey => $rowData) {
91 29
            $bothNumeric = self::numeric($lookupValue) && self::numeric($rowData[$column]);
92 29
            $bothNotNumeric = !self::numeric($lookupValue) && !self::numeric($rowData[$column]);
93 29
            $cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
94
95
            // break if we have passed possible keys
96
            if (
97 29
                $notExactMatch
98 29
                && (($bothNumeric && ($rowData[$column] > $lookupValue))
99 29
                || ($bothNotNumeric && ($cellDataLower > $lookupLower)))
100
            ) {
101 15
                break;
102
            }
103
104 26
            $rowNumber = self::checkMatch(
105 26
                $bothNumeric,
106 26
                $bothNotNumeric,
107 26
                $notExactMatch,
108 26
                $rowKey,
109 26
                $cellDataLower,
110 26
                $lookupLower,
111 26
                $rowNumber
112 26
            );
113
        }
114
115 29
        return $rowNumber;
116
    }
117
118
    private static function numeric(mixed $value): bool
119
    {
120
        return is_int($value) || is_float($value);
121
    }
122
}
123