@@ -421,7 +421,7 @@ public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $hei
421421 * @param mixed $index_num Specifies which value argument is selected.
422422 * Index_num must be a number between 1 and 254, or a formula or reference to a cell containing a number
423423 * between 1 and 254.
424- * @param mixed $value1... Value1 is required, subsequent values are optional.
424+ * @param mixed $value1 ... Value1 is required, subsequent values are optional.
425425 * Between 1 to 254 value arguments from which CHOOSE selects a value or an action to perform based on
426426 * index_num. The arguments can be numbers, cell references, defined names, formulas, functions, or
427427 * text.
@@ -709,24 +709,33 @@ public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not
709709
710710 $ rowNumber = $ rowValue = false ;
711711 foreach ($ lookup_array as $ rowKey => $ rowData ) {
712+ // break if we have passed possible keys
712713 if ((is_numeric ($ lookup_value ) && is_numeric ($ rowData [$ firstColumn ]) && ($ rowData [$ firstColumn ] > $ lookup_value )) ||
713714 (!is_numeric ($ lookup_value ) && !is_numeric ($ rowData [$ firstColumn ]) && (strtolower ($ rowData [$ firstColumn ]) > strtolower ($ lookup_value )))) {
714715 break ;
715716 }
716717 // remember the last key, but only if datatypes match
717718 if ((is_numeric ($ lookup_value ) && is_numeric ($ rowData [$ firstColumn ])) ||
718719 (!is_numeric ($ lookup_value ) && !is_numeric ($ rowData [$ firstColumn ]))) {
719- $ rowNumber = $ rowKey ;
720- $ rowValue = $ rowData [$ firstColumn ];
720+ if ($ not_exact_match ) {
721+ $ rowNumber = $ rowKey ;
722+ $ rowValue = $ rowData [$ firstColumn ];
723+
724+ continue ;
725+ } elseif ((strtolower ($ rowData [$ firstColumn ]) == strtolower ($ lookup_value ))
726+ // Spreadsheets software returns first exact match,
727+ // we have sorted and we might have broken key orders
728+ // we want the first one (by its initial index)
729+ && (($ rowNumber == false ) || ($ rowKey < $ rowNumber ))
730+ ) {
731+ $ rowNumber = $ rowKey ;
732+ $ rowValue = $ rowData [$ firstColumn ];
733+ }
721734 }
722735 }
723736
724737 if ($ rowNumber !== false ) {
725- if ((!$ not_exact_match ) && ($ rowValue != $ lookup_value )) {
726- // if an exact match is required, we have what we need to return an appropriate response
727- return Functions::NA ();
728- }
729- // otherwise return the appropriate value
738+ // return the appropriate value
730739 return $ lookup_array [$ rowNumber ][$ returnColumn ];
731740 }
732741
@@ -764,29 +773,35 @@ public static function HLOOKUP($lookup_value, $lookup_array, $index_number, $not
764773 if ((!is_array ($ lookup_array [$ firstRow ])) || ($ index_number > count ($ lookup_array ))) {
765774 return Functions::REF ();
766775 }
767- $ columnKeys = array_keys ( $ lookup_array [ $ firstRow ]);
776+
768777 $ firstkey = $ f [0 ] - 1 ;
769778 $ returnColumn = $ firstkey + $ index_number ;
770779 $ firstColumn = array_shift ($ f );
771-
772- if (!$ not_exact_match ) {
773- $ firstRowH = asort ($ lookup_array [$ firstColumn ]);
774- }
775- $ rowNumber = $ rowValue = false ;
780+ $ rowNumber = null ;
776781 foreach ($ lookup_array [$ firstColumn ] as $ rowKey => $ rowData ) {
777- if ((is_numeric ($ lookup_value ) && is_numeric ($ rowData ) && ($ rowData > $ lookup_value )) ||
778- (!is_numeric ($ lookup_value ) && !is_numeric ($ rowData ) && (strtolower ($ rowData ) > strtolower ($ lookup_value )))) {
782+ // break if we have passed possible keys
783+ $ bothNumeric = is_numeric ($ lookup_value ) && is_numeric ($ rowData );
784+ $ bothNotNumeric = !is_numeric ($ lookup_value ) && !is_numeric ($ rowData );
785+ if (($ bothNumeric && $ rowData > $ lookup_value ) ||
786+ ($ bothNotNumeric && strtolower ($ rowData ) > strtolower ($ lookup_value ))) {
779787 break ;
780788 }
781- $ rowNumber = $ rowKey ;
782- $ rowValue = $ rowData ;
783- }
784789
785- if ($ rowNumber !== false ) {
786- if ((!$ not_exact_match ) && ($ rowValue != $ lookup_value )) {
787- // if an exact match is required, we have what we need to return an appropriate response
788- return Functions::NA ();
790+ // Remember the last key, but only if datatypes match (as in VLOOKUP)
791+ if ($ bothNumeric || $ bothNotNumeric ) {
792+ if ($ not_exact_match ) {
793+ $ rowNumber = $ rowKey ;
794+
795+ continue ;
796+ } elseif (strtolower ($ rowData ) === strtolower ($ lookup_value )
797+ && ($ rowNumber === null || $ rowKey < $ rowNumber )
798+ ) {
799+ $ rowNumber = $ rowKey ;
800+ }
789801 }
802+ }
803+
804+ if ($ rowNumber !== null ) {
790805 // otherwise return the appropriate value
791806 return $ lookup_array [$ returnColumn ][$ rowNumber ];
792807 }
0 commit comments