Skip to content

Commit

Permalink
✨ array::fuzzyFilterSort
Browse files Browse the repository at this point in the history
  • Loading branch information
jcaillon committed Apr 18, 2024
1 parent cb6620d commit 441fb19
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 22 deletions.
6 changes: 3 additions & 3 deletions tests.d/1000-core-functions/results.approved.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,12 @@ declare -a lines=([0]="this is a word" [1]="very unbelievable" [2]="unbelievable
→ array::fuzzyFilter evle lines
declare -a LAST_RETURNED_ARRAY_VALUE=([0]="very unbelievable" [1]="unbelievable" [2]="ublievable")
declare -a LAST_RETURNED_ARRAY_VALUE2=([0]="1" [1]="3" [2]="4")
declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="1" [1]="1" [2]="1")
declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="0" [1]="0" [2]="0")
→ array::fuzzyFilter SC2 lines
declare -a LAST_RETURNED_ARRAY_VALUE=([0]="self mock2")
declare -a LAST_RETURNED_ARRAY_VALUE2=([0]="0")
declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="2")
declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="1")
→ array::fuzzyFilter u lines
declare -a LAST_RETURNED_ARRAY_VALUE=([0]="very unbelievable" [1]="unbelievable" [2]="ublievable")
Expand All @@ -125,7 +125,7 @@ declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="5" [1]="0" [2]="0")
→ array::fuzzyFilter seLf lines
declare -a LAST_RETURNED_ARRAY_VALUE=([0]="self mock1" [1]="self mock2")
declare -a LAST_RETURNED_ARRAY_VALUE2=([0]="0" [1]="0")
declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="1" [1]="1")
declare -a LAST_RETURNED_ARRAY_VALUE3=([0]="0" [1]="0")
→ array::fuzzyFilter nomatch lines
declare -a LAST_RETURNED_ARRAY_VALUE=()
Expand Down
67 changes: 58 additions & 9 deletions tests.d/1006-lib-array/00.tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# shellcheck source=../../valet.d/lib-array
source array

function testarray::sort() {
function testArray::sort() {

declare -g MYARRAY=(
breakdown
Expand All @@ -27,7 +27,7 @@ function testarray::sort() {
endTest "testArray::ing array::sort" 0
}

function testarray::appendIfNotPresent() {
function testArray::appendIfNotPresent() {

declare -g MYARRAY=(
breakdown
Expand Down Expand Up @@ -100,9 +100,9 @@ function testArray::makeArraysSameSize {
}

function testArray::sortWithCriteria() {
declare -g myArray=( a b c d e f g )
declare -g criteria1=( 3 2 2 1 1 4 0 )
declare -g criteria2=( 1 3 2 5 0 2 9 )
declare -g myArray=(a b c d e f g)
declare -g criteria1=(3 2 2 1 1 4 0)
declare -g criteria2=(1 3 2 5 0 2 9)

declare -p myArray criteria1 criteria2

Expand All @@ -113,15 +113,64 @@ function testArray::sortWithCriteria() {
declare -p myArray
echo "expected: g e d c b a f"

endTest "testArray::ing array::sortWithCriteria" 0
declare -a ARRAY_MATCHES=([0]="one the" [1]="the breakdown" [2]="holding the baby" [3]="the d day")
declare -a ARRAY_INDEXES=([0]="4" [1]="0" [2]="8" [3]="0")
declare -a ARRAY_DISTANCES=([0]="0" [1]="0" [2]="0" [3]="0")

declare -p ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES

echo
echo "→ array::sortWithCriteria ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES"
array::sortWithCriteria ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES

declare -p ARRAY_MATCHES

unset ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES

endTest "tesing array::sortWithCriteria" 0
}

function testArray::fuzzyFilterSort() {
myArray=(
"one the"
"the breakdown"
"constitutional"
"conventional"
"hold the baby"
"holiday inn"
"deliver"
"abundant"
"make a living"
"the d day"
"elevator"
)

declare -p myArray

echo
echo "→ array::fuzzyFilterSort the myArray"
array::fuzzyFilterSort the myArray

declare -p LAST_RETURNED_ARRAY_VALUE

echo
echo "→ array::fuzzyFilterSort elv myArray ⌜ ⌝"
array::fuzzyFilterSort elv myArray ⌜ ⌝

declare -p LAST_RETURNED_ARRAY_VALUE

unset myArray

endTest "testing array::fuzzyFilterSort" 0
}

function main() {
testarray::sort
testarray::appendIfNotPresent
testArray::sort
testArray::appendIfNotPresent
testArray::isInArray
testArray::makeArraysSameSize
testArray::sortWithCriteria
testArray::fuzzyFilterSort
}

main
main
44 changes: 44 additions & 0 deletions tests.d/1006-lib-array/results.approved.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,47 @@ declare -a array3=([0]="x" [1]="y" [2]="z" [3]="w")
declare -a array4=([0]="" [1]="" [2]="" [3]="")
```

### tesing array::sortWithCriteria

Exit code: `0`

**Standard** output:

```plaintext
declare -a myArray=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g")
declare -a criteria1=([0]="3" [1]="2" [2]="2" [3]="1" [4]="1" [5]="4" [6]="0")
declare -a criteria2=([0]="1" [1]="3" [2]="2" [3]="5" [4]="0" [5]="2" [6]="9")
→ array::sortWithCriteria myArray criteria1 criteria2
declare -a myArray=([0]="g" [1]="e" [2]="d" [3]="c" [4]="b" [5]="a" [6]="f")
expected: g e d c b a f
declare -a ARRAY_MATCHES=([0]="one the" [1]="the breakdown" [2]="holding the baby" [3]="the d day")
declare -a ARRAY_INDEXES=([0]="4" [1]="0" [2]="8" [3]="0")
declare -a ARRAY_DISTANCES=([0]="0" [1]="0" [2]="0" [3]="0")
→ array::sortWithCriteria ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES
declare -a ARRAY_MATCHES=([0]="the breakdown" [1]="the d day" [2]="one the" [3]="holding the baby")
```

### testing array::fuzzyFilterSort

Exit code: `0`

**Standard** output:

```plaintext
declare -a myArray=([0]="one the" [1]="the breakdown" [2]="constitutional" [3]="conventional" [4]="hold the baby" [5]="holiday inn" [6]="deliver" [7]="abundant" [8]="make a living" [9]="the d day" [10]="elevator")
→ array::fuzzyFilterSort the myArray
declare -a ARRAY_MATCHES=([0]="one the" [1]="the breakdown" [2]="hold the baby" [3]="the d day")
declare -a ARRAY_INDEXES=([0]="4" [1]="0" [2]="5" [3]="0")
declare -a ARRAY_DISTANCES=([0]="0" [1]="0" [2]="0" [3]="0")
declare -a LAST_RETURNED_ARRAY_VALUE=([0]="the breakdown" [1]="the d day" [2]="one the" [3]="hold the baby")
→ array::fuzzyFilterSort elv myArray ⌜ ⌝
declare -a ARRAY_MATCHES=([0]="d⌜e⌝⌜l⌝i⌜v⌝er" [1]="mak⌜e⌝ a ⌜l⌝i⌜v⌝ing" [2]="⌜e⌝⌜l⌝e⌜v⌝ator")
declare -a ARRAY_INDEXES=([0]="1" [1]="3" [2]="0")
declare -a ARRAY_DISTANCES=([0]="1" [1]="1" [2]="1")
declare -a LAST_RETURNED_ARRAY_VALUE=([0]="⌜e⌝⌜l⌝e⌜v⌝ator" [1]="d⌜e⌝⌜l⌝i⌜v⌝er" [2]="mak⌜e⌝ a ⌜l⌝i⌜v⌝ing")
```

5 changes: 4 additions & 1 deletion valet.d/commands.d/self-test-utils
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,10 @@ function runTest() {

# test if the user forgot to call endTest
if [[ -s "${_TEST_STANDARD_OUTPUT_FILE}" || -s "${_TEST_STANDARD_ERROR_FILE}" ]]; then
core::fail "The test script ⌜${testScript}⌝ did not call endTest OR it had outputs after the last endTest call."
local content1 content2
io::readFile "${_TEST_STANDARD_OUTPUT_FILE}" && content1="${LAST_RETURNED_VALUE}"
io::readFile "${_TEST_STANDARD_ERROR_FILE}" && content2="${LAST_RETURNED_VALUE}"
core::fail "The test script ⌜${testScript}⌝ did not call endTest OR it had outputs after the last endTest call."$'\n'"Standard output"$'\n'"${content1}"$'\n'"Error output:"$'\n'"${content2}"
fi

}
Expand Down
3 changes: 2 additions & 1 deletion valet.d/core
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ function core::sourceUserCommands() {
# core::reloadUserCommands
function core::reloadUserCommands() {
# delete previous variables
# shellcheck disable=SC2086
unset -v ${!CMD_*} _CMD_INCLUDED
core::sourceUserCommands
}
Expand Down Expand Up @@ -737,7 +738,7 @@ function array::fuzzyFilter() {
if [[ ${lineChar} == "${patternChar}" ]]; then
# if we find the character, mark the distance
distance=$((lineCharIndex - lastLineCharIndex))
lastLineCharIndex=${lineCharIndex}
lastLineCharIndex=$((lineCharIndex + 1))

# if it is the first char of the pattern, remmember the index
if [[ patternCharIndex -eq 0 ]]; then
Expand Down
146 changes: 139 additions & 7 deletions valet.d/lib-array
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function array::sort() {
# array::appendIfNotPresent myArray "c"
# echo "${myArray[@]}"
function array::appendIfNotPresent() {
local -n array="${1}"
local -n array=${1}
local value="${2}"

# add value to array if not present, check in a loop
Expand Down Expand Up @@ -138,24 +138,156 @@ function array::makeArraysSameSize() {
function array::sortWithCriteria() {
local -n array="${1}"
shift
local -a criteria=("$@")
local -a criteria=("${@}")

local -i i j k
local temp
local -a indexes

# create an array of indexes
for ((i = 0; i < ${#array[@]}; i++)); do
indexes+=("${i}")
done

# sort the indexes
for ((i = 0; i < ${#array[@]}; i++)); do
for ((j = i + 1; j < ${#array[@]}; j++)); do

for ((k = 0; k < ${#criteria[@]}; k++)); do
local -n criterion="${criteria[k]}"
if ((criterion[i] > criterion[j])); then
temp="${array[i]}"
array[i]="${array[j]}"
array[j]="${temp}"
if ((criterion[indexes[i]] > criterion[indexes[j]])); then
temp="${indexes[i]}"
indexes[i]="${indexes[j]}"
indexes[j]="${temp}"
break;
elif ((criterion[i] < criterion[j])); then
elif ((criterion[indexes[i]] < criterion[indexes[j]])); then
break;
fi
done
done
done

# create the sorted array
local -a sortedArray
for i in "${indexes[@]}"; do
sortedArray+=("${array[i]}")
done

array=("${sortedArray[@]}")
}


# Allows to fuzzy sort an array against a given pattern.
# Returns an array containing only the lines matching the pattern.
# The array is sorted by (in order):
# - the index of the first matched character in the line
# - the distance between the characters in the line
#
# $1: the pattern to match
# $2: the initial array name
# $3: (optional) string to add before each matched char
# $4: (optional) string to add after each matched char
#
# Returns:
# an the sorted and filtered in the global variable LAST_RETURNED_ARRAY_VALUE

# Usage:
# array::fuzzyMatch "pattern" "myarray" && local bestMatch="${LAST_RETURNED_VALUE}"
#
# Note:
# All characters in the pattern must be found in the same order in the matched line.
# The function is case insensitive.
# This function does not sort the results, it only filters them.
function array::fuzzyFilterSort() {
local pattern="${1}"
local -n array="${2}"
local beforeChar="${3:-}"
local afterChar="${4:-}"

ARRAY_MATCHES=()
ARRAY_INDEXES=()
ARRAY_DISTANCES=()

local -i patternLength lineLength
patternLength="${#pattern}"

# make all match case insensitive
shopt -s nocasematch

local line patternChar lineChar modifiedLine
local -i lineCharIndex patternCharIndex lastLineCharIndex distance patternFirstCharIndex

for line in "${array[@]}"; do
lineLength="${#line}"

modifiedLine=""

# for each character in the pattern
patternCharIndex=0
lineCharIndex=0
lastLineCharIndex=0
while [[ patternCharIndex -lt patternLength ]]; do
patternChar="${pattern:${patternCharIndex}:1}"

# find the character in the line
while [[ lineCharIndex -lt lineLength ]]; do
lineChar="${line:${lineCharIndex}:1}"

if [[ ${lineChar} == "${patternChar}" ]]; then
# modify the line
if ((lineCharIndex - lastLineCharIndex > 0)); then
modifiedLine+="${line:${lastLineCharIndex}:$((lineCharIndex - lastLineCharIndex))}"
fi
modifiedLine+="${beforeChar}${lineChar}${afterChar}"

# if we find the character, mark the distance
distance=$((lineCharIndex - lastLineCharIndex))
lastLineCharIndex=$((lineCharIndex + 1))

# if it is the first char of the pattern, remmember the index
if [[ patternCharIndex -eq 0 ]]; then
patternFirstCharIndex=${lineCharIndex}
fi

# loop on the next pattern character
break;
fi

lineCharIndex+=1
done

# if we don't find the character in the line, the line is not a match
if [[ lineCharIndex -ge lineLength ]]; then
break;
else
lineCharIndex+=1
fi

patternCharIndex+=1
done

# if we found all the characters in the pattern
if [[ patternCharIndex -ge patternLength ]]; then
# add the remaining characters in the line
if ((lineLength - lastLineCharIndex > 0)); then
modifiedLine+="${line:$((lastLineCharIndex)):$((lineLength - lastLineCharIndex))}"
fi

ARRAY_MATCHES+=("${modifiedLine}")
ARRAY_INDEXES+=("${patternFirstCharIndex}")
ARRAY_DISTANCES+=("${distance}")
fi

done

shopt -u nocasematch

declare -p ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES

# sort the results
array::sortWithCriteria ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES

LAST_RETURNED_ARRAY_VALUE=("${ARRAY_MATCHES[@]}")

unset ARRAY_MATCHES ARRAY_INDEXES ARRAY_DISTANCES
}
2 changes: 1 addition & 1 deletion valet.d/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.86
0.6.168

0 comments on commit 441fb19

Please sign in to comment.