1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
# frozen_string_literal: true
# Produces a unique set of values from an `Iterable` argument.
#
# * If the argument is a `String`, the unique set of characters are returned as a new `String`.
# * If the argument is a `Hash`, the resulting hash associates a set of keys with a set of unique values.
# * For all other types of `Iterable` (`Array`, `Iterator`) the result is an `Array` with
# a unique set of entries.
# * Comparison of all `String` values are case sensitive.
# * An optional code block can be given - if present it is given each candidate value and its return is used instead of the given value. This
# enables transformation of the value before comparison. The result of the lambda is only used for comparison.
# * The optional code block when used with a hash is given each value (not the keys).
#
# @example Using unique with a String
#
# ```puppet
# # will produce 'abc'
# "abcaabb".unique
# ```
#
# @example Using unique with an Array
#
# ```puppet
# # will produce ['a', 'b', 'c']
# ['a', 'b', 'c', 'a', 'a', 'b'].unique
# ```
#
# @example Using unique with a Hash
#
# ```puppet
# # will produce { ['a', 'b'] => [10], ['c'] => [20]}
# {'a' => 10, 'b' => 10, 'c' => 20}.unique
#
# # will produce { 'a' => 10, 'c' => 20 } (use first key with first value)
# Hash.new({'a' => 10, 'b' => 10, 'c' => 20}.unique.map |$k, $v| { [ $k[0] , $v[0]] })
#
# # will produce { 'b' => 10, 'c' => 20 } (use last key with first value)
# Hash.new({'a' => 10, 'b' => 10, 'c' => 20}.unique.map |$k, $v| { [ $k[-1] , $v[0]] })
# ```
#
# @example Using unique with an Iterable
#
# ```
# # will produce [3, 2, 1]
# [1,2,2,3,3].reverse_each.unique
# ```
#
# @example Using unique with a lambda
#
# ```puppet
# # will produce [['sam', 'smith'], ['sue', 'smith']]
# [['sam', 'smith'], ['sam', 'brown'], ['sue', 'smith']].unique |$x| { $x[0] }
#
# # will produce [['sam', 'smith'], ['sam', 'brown']]
# [['sam', 'smith'], ['sam', 'brown'], ['sue', 'smith']].unique |$x| { $x[1] }
#
# # will produce ['aBc', 'bbb'] (using a lambda to make comparison using downcased (%d) strings)
# ['aBc', 'AbC', 'bbb'].unique |$x| { String($x,'%d') }
#
# # will produce {[a] => [10], [b, c, d, e] => [11, 12, 100]}
# {a => 10, b => 11, c => 12, d => 100, e => 11}.unique |$v| { if $v > 10 { big } else { $v } }
# ```
#
# Note that for `Hash` the result is slightly different than for the other data types. For those the result contains the
# *first-found* unique value, but for `Hash` it contains associations from a set of keys to the set of values clustered by the
# equality lambda (or the default value equality if no lambda was given). This makes the `unique` function more versatile for hashes
# in general, while requiring that the simple computation of "hash's unique set of values" is performed as `$hsh.map |$k, $v| { $v }.unique`.
# (Generally, it's meaningless to compute the unique set of hash keys because they are unique by definition. However, the
# situation can change if the hash keys are processed with a different lambda for equality. For this unique computation,
# first map the hash to an array of its keys.)
# If the more advanced clustering is wanted for one of the other data types, simply transform it into a `Hash` as shown in the
# following example.
#
# @example turning a string or array into a hash with index keys
#
# ```puppet
# # Array ['a', 'b', 'c'] to Hash with index results in
# # {0 => 'a', 1 => 'b', 2 => 'c'}
# Hash(['a', 'b', 'c'].map |$i, $v| { [$i, $v]})
#
# # String "abc" to Hash with index results in
# # {0 => 'a', 1 => 'b', 2 => 'c'}
# Hash(Array("abc").map |$i,$v| { [$i, $v]})
# "abc".to(Array).map |$i,$v| { [$i, $v]}.to(Hash)
# ```
#
# @since Puppet 5.0.0
#
Puppet::Functions.create_function(:unique) do
dispatch :unique_string do
param 'String', :string
optional_block_param 'Callable[String]', :block
end
dispatch :unique_hash do
param 'Hash', :hash
optional_block_param 'Callable[Any]', :block
end
dispatch :unique_array do
param 'Array', :array
optional_block_param 'Callable[Any]', :block
end
dispatch :unique_iterable do
param 'Iterable', :iterable
optional_block_param 'Callable[Any]', :block
end
def unique_string(string, &block)
string.split('').uniq(&block).join('')
end
def unique_hash(hash, &block)
block = ->(v) { v } unless block_given?
result = Hash.new { |h, k| h[k] = { :keys => [], :values => [] } }
hash.each_pair do |k, v|
rc = result[block.call(v)]
rc[:keys] << k
rc[:values] << v
end
# reduce the set of possibly duplicated value entries
inverted = {}
result.each_pair { |_k, v| inverted[v[:keys]] = v[:values].uniq }
inverted
end
def unique_array(array, &block)
array.uniq(&block)
end
def unique_iterable(iterable, &block)
Puppet::Pops::Types::Iterable.on(iterable).uniq(&block)
end
end
|