1

After seeing this question, I decided to put together a function to remove array empty elements in case it saves someone a few seconds.

Is there any way to return (or export) a dynamically named input array-variable as the output of a function?

Ideally

  1. User calls: removeArrayBlanks "newArrayName" "arrayItem1" "" "arrayItem2"...
  2. The function unsets the old array and creates: ${newArrayName[@]}, which expands to "arrayItem1" "arrayItem2" without any blank items or non-sequential index numbers

Also, does anyone have any optimizations/suggestions to the function? I've included the function below. Thanks!

removeArrayBlanks() {
    # Usage: Provide array as input, store output as array. 
    # Example 1: mapfile -t lastArray < <(removeArrayBlanks "hate" "" "empty" "array" "items")
    # Example 2: mapfile -t lastArray < <(removeArrayBlanks "${inputArray[@]}")
    [[ $# -lt 2 ]] && echo "Usage: Provide array as an argument." && exit 1 # Ensure input is array
    tempArray=( "$@" ) # Rebuild array from inputs
    for i in "${!tempArray[@]}"; do
        [[ ! -z "${tempArray[$i]}" ]] && finalArray+=( "${tempArray[$i]}" ) # Add non-empty strings
    done && printf '%s\n' "${finalArray[@]}" && unset tempArray && unset finalArray
}

user1934428
  • 19,864
  • 7
  • 42
  • 87
jmtornetta
  • 85
  • 6
  • Unless you declare it local, variables assigned in a function are visible in the main script, so you don't need to return it. – Barmar Aug 02 '21 at 20:58
  • do you want to remove the 'blanks' from the original array, or create a brand new array with no 'blanks'? – markp-fuso Aug 02 '21 at 21:01
  • `! -z` seems odd. – Jetchisel Aug 02 '21 at 21:03
  • @Barmar ty, was have a hard time getting the new array to be called `$1` from user input. I couldn't find an easy way to 'update' the input array, without forcing the user to have to call a new array name. – jmtornetta Aug 02 '21 at 21:06
  • @markp-fus trying to just remove blanks from input array. And, ideally, not have to rename it or declare a new array. – jmtornetta Aug 02 '21 at 21:07
  • @Jetchisel it is kinda like a double negative, i hear ya. Was trying to find the best way possible to take non-empty values and put them into a new , fresh array without non-sequential index numbers or empty items. – jmtornetta Aug 02 '21 at 21:08
  • @jmtornetta : I took the liberty to remove the _sh_ tag, since the question is not related to `sh` (after all, _sh_ does not have arrays). – user1934428 Aug 03 '21 at 05:55

1 Answers1

2

Assumptions:

  • objective is to remove array elements that contain empty strings (aka 'blanks') from current array
  • function should be able to work for any array (name) passed to the function
  • OP is running, or can upgrade to/above, bash 4.3+ (needed for nameref support)

NOTE: to 'remove' an array element just unset the array reference (eg, unset array[index#1]

One idea using a nameref in the function and the unset command:

function removeArrayBlanks() {

declare -n localarray="${1}"                                  # define nameref; remains a local array variable

for i in "${!localarray[@]}"                                  # loop through indices
do
    [[ -z "${localarray[${i}]}" ]] && unset localarray[${i}]  # remove entries that contain empty strings
done
}

In operation:

$ myarray=( a "" 1 "" A )
$ typeset -p myarray
declare -a myarray=([0]="a" [1]="" [2]="1" [3]="" [4]="A")

$ removeArrayBlanks myarray
$ typeset -p myarray
declare -a myarray=([0]="a" [2]="1" [4]="A")

# verify localarray not known by parent
$ typeset -p localarray
-bash: typeset: localarray: not found
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
  • Thanks @markp-fuso! I didn't realize that the variable would be updated in the current context/shell from which the function was called. I figured I would have had to 'export' the new array somehow. – jmtornetta Aug 02 '21 at 21:24
  • Ahh, I see...I suppose I wasn't wrong? I just didn't realized that this was possible: `declare -n` "Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except those using or changing the -n attribute itself, are performed on the variable referenced by name's value." – jmtornetta Aug 02 '21 at 21:27
  • keep in mind a function call is (in essence) a macro call (think `replace '' with ''`) and not a sub-process call; net result is anything you do in the function is actually applied in the 'parent' process; one exception is if you define a variable as 'local' to the function in which case the 'parent' does not see it; in this case the `nameref' is a 'local' variable that also doubles as a **reference** (and not a copy) of the passed in array name – markp-fuso Aug 02 '21 at 21:31
  • I opted to populate the array first and then remove/unset the empty strings; you could still implement your logic ... filter out the empty strings before assiging values to the array ... via a function, but also use a `nameref` to eliminate the overhead of creating/printing/trashing 'temporary' arrays ... your choice – markp-fuso Aug 02 '21 at 21:34
  • Yes, adding in the nameref was one thing I didn't realize. The other thing I am seeing that I missed was not expanding out the array as function argument. That is, I was doing `removeArrayBlanks ${inputArray[@]}` which I gather would cause the array name to be lost, because the array is expanded before function can do stuff to it. – jmtornetta Aug 02 '21 at 21:40