Skip to content

Commit 0fd4555

Browse files
author
Andreas Tharang
committed
added: _ArrayJoin()
1 parent 2b2557c commit 0fd4555

File tree

3 files changed

+330
-10
lines changed

3 files changed

+330
-10
lines changed

ArrayPlus.au3

+310-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
; #INDEX# =======================================================================================================================
77
; Title .........: ArrayPlus-UDF
8-
; Version .......: 0.5
8+
; Version .......: 0.6
99
; AutoIt Version : 3.3.16.1
1010
; Language ......: english (german maybe by accident)
1111
; Description ...: advanced helpers for array handling
1212
; Author(s) .....: AspirinJunkie
13-
; Last changed ..: 2023-09-02
13+
; Last changed ..: 2024-02-12
1414
; Link ..........: https://github.com/Sylvan86/autoit-arrayplus-udf
1515
; License .......: This work is free.
1616
; You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2,
@@ -31,6 +31,7 @@
3131
; _ArrayAinATo2d - convert array-in-array into a 2D array
3232
; _Array2String - print a 1D/2D-array to console or variable clearly arranged
3333
; _ArrayAlignDec - align a 1D-array or a column of a 2D-array at the decimal point or right aligned
34+
; _ArrayJoin - sql-like joins for AutoIt-Arrays
3435
; _ArrayMap - apply a function to every element of a array ("map" the function)
3536
; _ArrayReduce - reduce the elements of a array to one value with an external function
3637
; _ArrayFilter - filter the elements of an array with a external function
@@ -734,6 +735,293 @@ Func _ArrayAinATo2d(ByRef $A)
734735
Return SetExtended($N, $a_Ret)
735736
EndFunc ;==>_ArrayAinATo2d
736737

738+
; #FUNCTION# ======================================================================================
739+
; Name ..........: _ArrayJoin()
740+
; Description ...: Combines 2 2D arrays via corresponding properties (actual data or user-defined calculated) similar to JOIN in relational databases
741+
; Syntax ........: _ArrayJoin($aA, $aB, [$vCompA = 0, [$vCompB = Default, [$sJoinType = "inner"]]])
742+
; Parameters ....: $aA - 2D array or Array-In-Array which should joined with $aB
743+
; $aB - 2D array or Array-In-Array which should joined with $aA
744+
; $vCompA - Rule for determining the link key for $aA.
745+
; defaults to 0 = value of first column element
746+
; Can be:
747+
; | single Integer: Column-Index for direct values
748+
; | Integer-Array: combined multiple direct values
749+
; | user-defined function: calculation rule as a function of the form
750+
; function($a1DArray, $dummy): get the current array row as 1D-Array and calculate the key
751+
; | String: AutoIt-Code as string to calculate the key
752+
; Must contain "$A", where "$A" represents the current array line as a 1D array.
753+
; $vCompB - the same like $vCompA but for Array $aB
754+
; defaults to the same value as $aA
755+
; $sJoinType - type of joining (see https://www.w3schools.com/sql/sql_join.asp for explanation)
756+
; on of these:
757+
; | "inner": inner join - Returns records that have matching values in both tables
758+
; | "left" : left (outer) join - Returns all records from the left table, and the matched records from the right table
759+
; | "right": right (outer) join - Returns all records from the right table, and the matched records from the left table
760+
; | "outer" or "full": (full) outer join - Returns all records when there is a match in either left or right table
761+
; Return values .: Success: combined values as 2D-Array with columns of $aA first and $aB following. @extended = number of rows
762+
; Failure: null and set error to:
763+
; | @error = 1 : $aA is not a 1D/2D array
764+
; | @error = 2 : $aB is not a 1D/2D array
765+
; | @error = 3 : No data in $aA
766+
; | @error = 4 : No data in $aB
767+
; | @error = 5 : $aA is not a valid array-in-array
768+
; | @error = 6 : $aB is not a valid array-in-array
769+
; | @error = 7 : no valid form for $vCompA
770+
; | @error = 8 : no valid form for $vCompB
771+
; | @error = 9 : $vCompA is a string but it does not contain $A
772+
; | @error = 10: $vCompB is a string but it does not contain $A
773+
; | @error = 11: An array was passed for $vCompA but not a 1D array - invalid
774+
; | @error = 12: An array was passed for $vCompB but not a 1D array - invalid
775+
; | @error = 13: no valid value for $sJoinType passed
776+
; | @error = 14: No joins found - return array is therefore empty
777+
; Author ........: aspirinjunkie
778+
; Modified ......: 2024-02-12
779+
; Related .......: __ap_cb_getKey_Index_Single(), _Array__ap_cb_getKey_String(), __ap_cb_getKey_Index_Multi(), _Array2dToAinA()
780+
; Example .......: Yes
781+
; Global $aProds[][3] = [["apple", 1.5, "fruits"], ["Pancake", 3.0, "sweets"], ["banana", 2.0, "fruits"], ["banana", 1.0, "plastic"]]
782+
; Global $aColors[][2] = [["apple", "red"], ["coconut", "brown"], ["banana", "yellow"], ["banana", "brown"]]
783+
;
784+
; ; full outer join by first value of both arrays
785+
; $aJoin = _ArrayJoin($aProds, $aColors, 0, 0, "outer")
786+
; _ArrayDisplay($aJoin, "by first value")
787+
;
788+
; ; join using the first letter of first value in both arrays
789+
; $aJoin = _ArrayJoin($aProds, $aColors, "StringLeft($A[0], 1)")
790+
; _ArrayDisplay($aJoin, "by first letter")
791+
; =================================================================================================
792+
Func _ArrayJoin($aA, $aB, $vCompA = 0, $vCompB = Default, $sJoinType = "inner")
793+
Local $bCbIsString = False
794+
795+
; same key descriptor for both arrays (if $vCompB = Default)
796+
If IsKeyword($vCompB) = 1 Then $vCompB = $vCompA
797+
798+
; variables which describe the both Arrays
799+
Local $nDimsA = UBound($aA, 0), $nDimsB = UBound($aB, 0), _
800+
$nRowsA = UBound($aA, 1), $nRowsB = UBound($aB, 1), _
801+
$nColsA = UBound($aA, 2), $nColsB = UBound($aB, 2)
802+
803+
If $nRowsA < 1 Then Return SetError(3, $nRowsA, Null)
804+
If $nRowsB < 1 Then Return SetError(4, $nRowsB, Null)
805+
806+
; prepare Array A (convert into Array-In-Array)
807+
Switch $nDimsA
808+
Case 1 ; already Array-In-Array
809+
If Not IsArray($nRowsA[0]) Then Return SetError(5, 0, Null)
810+
811+
Case 2 ; 2D-Array in Array-In-Array
812+
; already dimension the number of sub-elements for the result array
813+
ReDim $aA[$nRowsA][$nColsA + $nColsB]
814+
815+
; convert into Array-In-Array for better handling in the next steps
816+
$aA = _Array2dToAinA($aA)
817+
818+
Case Else
819+
Return SetError(1, $nDimsA, Null)
820+
821+
EndSwitch
822+
823+
; prepare Array B (convert into Array-In-Array)
824+
Switch $nDimsB
825+
Case 1 ; already Array-In-Array
826+
If Not IsArray($nRowsB[0]) Then Return SetError(6, 0, Null)
827+
828+
Case 2
829+
; convert into Array-In-Array for better handling in the next steps
830+
$aB = _Array2dToAinA($aB)
831+
832+
Case Else
833+
Return SetError(2, $nDimsB, Null)
834+
835+
EndSwitch
836+
837+
; prepare the key extraction function for $aA
838+
Local $cbKeyA
839+
Select
840+
Case IsInt($vCompA) ; single array index as key
841+
$cbKeyA = __ap_cb_getKey_Index_Single
842+
843+
Case IsFunc($vCompA) ; user defined function
844+
$cbKeyA = $vCompA
845+
846+
Case IsString($vCompA) ; function directly as a string
847+
If Not StringInStr($vCompA, '$A', 2) Then Return SetError(9, 0, Null)
848+
Local $bBefore = Opt("ExpandEnvStrings", 1)
849+
$cbKeyA = __ap_cb_getKey_String
850+
$bCbIsString = True
851+
852+
Case IsArray($vCompA) ; multiple indices
853+
If UBound($vCompA, 0) <> 1 Then Return SetError(11, UBound($vCompA, 0), Null)
854+
$cbKeyA = __ap_cb_getKey_Index_Multi
855+
856+
Case Else ; no valid form for $vCompA
857+
Return SetError(7, 0, Null)
858+
859+
EndSelect
860+
861+
; prepare the key extraction function for $aB
862+
Local $cbKeyB
863+
Select
864+
Case IsInt($vCompB) ; single array index as key
865+
$cbKeyB = __ap_cb_getKey_Index_Single
866+
867+
Case IsFunc($vCompB) ; user defined function
868+
$cbKeyB = $vCompB
869+
870+
Case IsString($vCompB) ; function directly as a string
871+
If Not StringInStr($vCompB, '$A', 2) Then Return SetError(10, 0, Null)
872+
Local $bBefore = Opt("ExpandEnvStrings", 1)
873+
$cbKeyB = __ap_cb_getKey_String
874+
$bCbIsString = True
875+
876+
Case IsArray($vCompB) ; multiple indices
877+
If UBound($vCompB, 0) <> 1 Then Return SetError(12, UBound($vCompB, 0), Null)
878+
$cbKeyB = __ap_cb_getKey_Index_Multi
879+
880+
Case Else ; no valid form for $vCompB
881+
Return SetError(8, 0, Null)
882+
883+
EndSelect
884+
885+
; convert $aA into Map
886+
Local $mA[], $aData, $sKey
887+
For $i = 0 To $nRowsA - 1
888+
$aData = $aA[$i]
889+
$sKey = $cbKeyA($aData, $vCompA)
890+
891+
Local $aSubElements[1]
892+
If MapExists($mA, $sKey) Then ; record with same key already exists
893+
$aSubElements = $mA[$sKey]
894+
ReDim $aSubElements[UBound($aSubElements) + 1]
895+
EndIf
896+
897+
$aSubElements[UBound($aSubElements) - 1] = $aData
898+
$mA[$sKey] = $aSubElements
899+
900+
Next
901+
902+
; convert $aB into Map
903+
Local $mB[]
904+
For $i = 0 To $nRowsB - 1
905+
$aData = $aB[$i]
906+
$sKey = $cbKeyB($aData, $vCompB)
907+
908+
Local $aSubElements[1]
909+
If MapExists($mB, $sKey) Then ; record with same key already exists
910+
$aSubElements = $mB[$sKey]
911+
ReDim $aSubElements[UBound($aSubElements) + 1]
912+
EndIf
913+
914+
$aSubElements[UBound($aSubElements) - 1] = $aData
915+
$mB[$sKey] = $aSubElements
916+
917+
Next
918+
919+
; join both arrays
920+
Local $mRet[], $aDataTmpA, $aDataTmpB
921+
Switch $sJoinType
922+
Case "inner", "left", "outer", "full"
923+
For $sKey In MapKeys($mA)
924+
If $sJoinType = "inner" And Not MapExists($mB, $sKey) Then ContinueLoop
925+
926+
$aSubA = $mA[$sKey]
927+
$aSubB = $mB[$sKey]
928+
929+
For $i = 0 To UBound($aSubA) - 1
930+
$aDataTmpA = $aSubA[$i]
931+
932+
If IsArray($aSubB) Then ; corresponding right data
933+
For $j = 0 To UBound($aSubB) - 1
934+
$aDataTmpB = $aSubB[$j]
935+
936+
For $k = 0 To UBound($aDataTmpB) - 1
937+
$aDataTmpA[$k + $nColsA] = $aDataTmpB[$k]
938+
Next
939+
MapAppend($mRet, $aDataTmpA)
940+
Next
941+
942+
Else ; left join without corresponding right data
943+
MapAppend($mRet, $aDataTmpA)
944+
EndIf
945+
Next
946+
Next
947+
948+
; for outer join first do the left join, then add the rest
949+
If $sJoinType = "outer" Or $sJoinType = "full" Then ContinueCase
950+
951+
Case "outer", "full"
952+
; after left join add the rest
953+
For $sKey In MapKeys($mB)
954+
If MapExists($mA, $sKey) Then ContinueLoop
955+
956+
$aSubB = $mB[$sKey]
957+
958+
For $i = 0 To UBound($aSubB) - 1
959+
Local $aData[$nColsA + $nColsB]
960+
$aDataTmpB = $aSubB[$i]
961+
962+
For $k = 0 To UBound($aDataTmpB) - 1
963+
$aData[$k + $nColsA] = $aDataTmpB[$k]
964+
Next
965+
966+
MapAppend($mRet, $aData)
967+
Next
968+
Next
969+
970+
Case "right"
971+
For $sKey In MapKeys($mB)
972+
973+
$aSubA = $mA[$sKey]
974+
$aSubB = $mB[$sKey]
975+
976+
For $i = 0 To UBound($aSubB) - 1
977+
Local $aData[$nColsA + $nColsB]
978+
979+
$aDataTmpB = $aSubB[$i]
980+
981+
For $k = 0 To UBound($aDataTmpB) - 1
982+
$aData[$k + $nColsA] = $aDataTmpB[$k]
983+
Next
984+
985+
If IsArray($aSubA) Then ; corresponding left data
986+
For $j = 0 To UBound($aSubA) - 1
987+
$aDataTmpA = $aSubA[$j]
988+
989+
For $k = 0 To $nColsA - 1
990+
$aData[$k] = $aDataTmpA[$k]
991+
Next
992+
MapAppend($mRet, $aData)
993+
Next
994+
995+
Else ; right join without corresponding left data
996+
MapAppend($mRet, $aData)
997+
EndIf
998+
Next
999+
Next
1000+
1001+
Case Else
1002+
Return SetError(13, 0, Null)
1003+
EndSwitch
1004+
1005+
; build the return Array
1006+
Local $nRowsRet = UBound($mRet), $nColsRet = $nColsA + $nColsB
1007+
Local $aRet[UBound($mRet)][$nColsRet], $iArr = 0
1008+
If $mRet < 1 Then Return SetError(14, $nRowsRet, $aRet)
1009+
1010+
For $sKey In MapKeys($mRet)
1011+
$aDataTmp = $mRet[$sKey]
1012+
1013+
For $i = 0 To $nColsRet - 1
1014+
$aRet[$iArr][$i] = $aDataTmp[$i]
1015+
Next
1016+
1017+
$iArr += 1
1018+
Next
1019+
1020+
If $bCbIsString Then Opt("ExpandEnvStrings", $bBefore)
1021+
1022+
Return SetExtended(UBound($mRet), $aRet)
1023+
EndFunc ;==>_ArrayJoin
1024+
7371025
; #FUNCTION# ======================================================================================
7381026
; Name ..........: _ArrayMap
7391027
; Description ...: apply a function to every element of a array ("map" the function)
@@ -2198,6 +2486,26 @@ Func __ap_cb_comp_String(ByRef $A, Const $b = Default)
21982486
Return $vRet
21992487
EndFunc
22002488

2489+
; helper function for _ArrayJoin() which generates a primary key from a single array index
2490+
Func __ap_cb_getKey_Index_Single(ByRef Const $aA, Const $iInd)
2491+
Return $aA[$iInd]
2492+
EndFunc ;==>__ap_cb_getKey_Index_Single
2493+
2494+
; helper function for _ArrayJoin() which generates a primary key from several array indices
2495+
Func __ap_cb_getKey_Index_Multi(ByRef Const $aA, Const $aInd)
2496+
Local $sKey = ""
2497+
For $i = 0 To UBound($aInd) - 1
2498+
$sKey &= $aA[$aInd[$i]] & "|"
2499+
Next
2500+
Return StringTrimRight($sKey, 1)
2501+
EndFunc ;==>__ap_cb_getKey_Index_Multi
2502+
2503+
; helper function for _ArrayJoin() which determines a primary key from a calculation rule as AutoIt code in the string $sCBString
2504+
Func __ap_cb_getKey_String(ByRef Const $A, Const $sCBSTRING)
2505+
Local $vRet = Execute($sCBSTRING)
2506+
Return SetError(@error, @extended, $vRet)
2507+
EndFunc ;==>__ap_cb_getKey_String
2508+
22012509
; #FUNCTION# ======================================================================================
22022510
; Name ..........: __ap_swap
22032511
; Description ...: helper function for swap two values inside an array

0 commit comments

Comments
 (0)