File: unique.rb

package info (click to toggle)
puppet-agent 8.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,404 kB
  • sloc: ruby: 286,820; sh: 492; xml: 116; makefile: 88; cs: 68
file content (135 lines) | stat: -rw-r--r-- 4,876 bytes parent folder | download | duplicates (2)
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