#!/bin/bash
#  list - Bash array support

set -e


######
# List helpers

# in_list() - Returns success if the item ($1) is found in the list ($@).
in_list() {
	min_args 1 "$@"
	local thing=$1
	shift
	local found=false
	for i in "$@"; do
		[ "$i" != "$thing" ] || found=true
	done
	$found
}

# list_insert() - Inserts items ($@) at the beginning of the named array ($1).
list_insert() {
	min_args 1 "$@"
	local _list_insert_var=$1
	shift
	eval "$_list_insert_var=( \"\$@\" "\${$_list_insert_var\[@\]}" )"
}

# list_append() - Appends items ($@) to the end of the named array ($1).
list_append() {
	min_args 1 "$@"
	local _list_append_var=$1
	shift
	eval "$_list_append_var=( \"\${${_list_append_var}[@]}\" \"\$@\" )"
}

# list_pop() - Removes and prints the last item from the named array ($1).
list_pop() {
	has_args 1 "$@"
	local _list_var=$1
	shift
	local _value
	eval "_value=\"\${${_list_var}[-1]}\""

	local length
	eval "length=\${#${_list_var}[*]}"
	eval "$_list_var=( \"\${${_list_var}[@]:0:$(($length - 1))}\" )"
	echo "$_value"
}

# list_fpop() - Removes and prints the first item from the named array ($1).
list_fpop() {
	has_args 1 "$@"
	local _list_var=$1
	shift
	local _value
	eval "_value=\"\${${_list_var}[0]}\""
	eval "$_list_var=( "\${${_list_var}[@]:1}" )"
	echo "$_value"
}


######
# List Iteration

# for_each() - Calls a function ($1) for each item ($@).
for_each() {
	min_args 1 "$@"
	local func=$1
	shift
	local _for_each
	for _for_each in "$@"; do
		$func "$_for_each"
	done
}

# run_for_each() - Calls a function ($1) using ``run()`` for each item ($@).
run_for_each() {
	local func=$1
	shift
	for_each "run $func" "$@"
}

# for_each_pair() - Calls a function ($1) for each pair of arguments ($@).
# If an odd number of arguments are given, an error will be produced.
for_each_pair() {
	local func=$1
	shift

	[ $((${#*} % 2)) == 0 ] || error "odd number of arguments"

	while [ ${#*} -gt 0 ]; do
		$func "$1" "$2"
		shift 2
	done
}


######
# List Transformations

# list_reverse() - Reverse the order of items in the named array ($1).
list_reverse() {
	min_max_args 1 2 "$@"
	local __src=$1
	local __dst=${2:-$1}

	local -a src
	eval "src=( \"\${${__src}[@]}\" )"

	local i
	local -a dst
	for i in $(seq $((${#src[@]} - 1)) -1 0); do
		dst+=( "${src[$i]}" )
	done
	eval "$__dst=( \"\${dst[@]}\" )"
}

# list_quote() - Quotes the items in the named list ($1).
list_quote() {
	min_max_args 1 2 "$@"
	list_map arg_quote "$@"
}

# list_join() - Joins arguments using the given character
# TODO: rename str_join?
#  $1 - character to use for joining arguments
#  $@ - strings to be joined by $1
list_join() {
	local IFS=$1
	shift
	echo "$*"
}

# list_filter() - Runs a filter ($1) for each item in the named array
# ($2). If the filter function returns success, the item will be
# included in the output array ($3, or $2 if not given).
# $1 - Filter command
# $2 - Input list
# $3 - Output list (optional)
list_filter() {
	min_max_args 2 3 "$@"
	local __func=$1
	local __list=$2
	local __dest=${3:-$2}
	local -a __out
	eval "for_each _list_filter \"\${${__list}[@]}\""
	eval "$__dest=( \"\${__out[@]}\" )"
}
_list_filter() {
	local __item=$1
	if $__func "$__item"; then
		list_append __out "$__item"
	fi
}

# list_filter_empty() - Removes empty items from the named array ($1) and
# saves them in the output array ($2, or $1 if not given).
# $1 - Input list
# $2 - Output list (optional)
list_filter_empty() {
	min_max_args 1 2 "$@"
	list_filter _list_filter_empty "$@"
}
_list_filter_empty() { [ "$1" ]; }

# list_unique() - Removes duplicate items from the named array ($1) and
# save them in the output array ($2, or $1 if not given).
# $1 - Input list
# $2 - Output list (optional)
list_unique() {
	min_max_args 1 2 "$@"
	local -A __found
	list_filter _list_unique_check "$@"
}
_list_unique_check() {
	local item=$1
	if [ -z "${__found[$item]}" ]; then
		__found[$item]=1
		true
	else
		false
	fi
}

# list_map() - Runs a command ($1) with each of the items in the named
# array ($2).  The output of each command invocation is stored in the
# output array ($3, or $2 if not given)
# $1 - Filter command
# $2 - Input list
# $3 - Output list (optional)
list_map() {
	min_max_args 2 3 "$@"
	local __func=$1
	local __list=$2
	local __dest=${3:-$2}

	local -a __in
	eval "__in=( \"\${${__list}[@]}\" )"

	local -a __out
	for_each _list_map "${__in[@]}"

	eval "$__dest=( \"\${__out[@]}\" )"
}
_list_map() {
	local __item=$1
	local __value
	__value=$($__func "$__item")
	list_append __out "$__value"
}

# list_copy_or_args - Copies arguments ($@) to output list ($1). If
# no arguments are given, copies the default list ($2) to the output list.
# $1 - name of output list
# $2 - name of default list
# $@ - optional arguments to copy to output list
list_copy_or_args() {
	min_args 2 "$@"
	local lcoa_dst=$1
	local lcoa_src=$2
	shift 2

	local lcoa_var="@"
	[ "$*" ] || lcoa_var="{lcoa_src[@]}"
	eval "$lcoa_dst=( \"\$$lcoa_var\" )"
}

# list_from_func_or_args - Copies arguments ($@) to output list ($1). If
# no arguments are given, runs the given function ($2) to populate that list.
# $1 - name of output list
# $2 - name of function to provide defaults
# $@ - optional arguments to copy to output list
list_from_func_or_args() {
	min_args 2 "$@"
	local var=$1
	local func=$2
	shift 2
	if [ "$*" ]; then
		eval "$var=( \"\$@\" )"
	else
		eval "$var=( \$($func) )"
	fi
}

View the Developer Guide Index

View the Reference Manual Index


Generated on Fri Jul 28 14:34:59 PDT 2017 by mcsh d14 v0.23.0.