|
5 | 5 |
|
6 | 6 | ; #INDEX# =======================================================================================================================
|
7 | 7 | ; Title .........: ArrayPlus-UDF
|
8 |
| -; Version .......: 0.5 |
| 8 | +; Version .......: 0.6 |
9 | 9 | ; AutoIt Version : 3.3.16.1
|
10 | 10 | ; Language ......: english (german maybe by accident)
|
11 | 11 | ; Description ...: advanced helpers for array handling
|
12 | 12 | ; Author(s) .....: AspirinJunkie
|
13 |
| -; Last changed ..: 2023-09-02 |
| 13 | +; Last changed ..: 2024-02-12 |
14 | 14 | ; Link ..........: https://github.com/Sylvan86/autoit-arrayplus-udf
|
15 | 15 | ; License .......: This work is free.
|
16 | 16 | ; 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 | 31 | ; _ArrayAinATo2d - convert array-in-array into a 2D array
|
32 | 32 | ; _Array2String - print a 1D/2D-array to console or variable clearly arranged
|
33 | 33 | ; _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 |
34 | 35 | ; _ArrayMap - apply a function to every element of a array ("map" the function)
|
35 | 36 | ; _ArrayReduce - reduce the elements of a array to one value with an external function
|
36 | 37 | ; _ArrayFilter - filter the elements of an array with a external function
|
@@ -734,6 +735,293 @@ Func _ArrayAinATo2d(ByRef $A)
|
734 | 735 | Return SetExtended($N, $a_Ret)
|
735 | 736 | EndFunc ;==>_ArrayAinATo2d
|
736 | 737 |
|
| 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 | + |
737 | 1025 | ; #FUNCTION# ======================================================================================
|
738 | 1026 | ; Name ..........: _ArrayMap
|
739 | 1027 | ; 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)
|
2198 | 2486 | Return $vRet
|
2199 | 2487 | EndFunc
|
2200 | 2488 |
|
| 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 | + |
2201 | 2509 | ; #FUNCTION# ======================================================================================
|
2202 | 2510 | ; Name ..........: __ap_swap
|
2203 | 2511 | ; Description ...: helper function for swap two values inside an array
|
|
0 commit comments