Update stdlib and concat to 6.1.0 both
[mirror/dsa-puppet.git] / 3rdparty / modules / stdlib / lib / puppet / functions / merge.rb
1 # @summary
2 #   Merges two or more hashes together or hashes resulting from iteration, and returns
3 #   the resulting hash.
4 #
5 # @example Using merge()
6 #   $hash1 = {'one' => 1, 'two', => 2}
7 #   $hash2 = {'two' => 'dos', 'three', => 'tres'}
8 #   $merged_hash = merge($hash1, $hash2) # $merged_hash =  {'one' => 1, 'two' => 'dos', 'three' => 'tres'}
9 #
10 # When there is a duplicate key, the key in the rightmost hash will "win."
11 #
12 # Note that since Puppet 4.0.0 the same merge can be achieved with the + operator.
13 #  `$merged_hash = $hash1 + $hash2`
14 #
15 # If merge is given a single Iterable (Array, Hash, etc.) it will call a given block with
16 # up to three parameters, and merge each resulting Hash into the accumulated result. All other types
17 # of values returned from the block (typically undef) are skipped (not merged).
18 #
19 # The codeblock can take 2 or three parameters:
20 # * with two, it gets the current hash (as built to this point), and each value (for hash the value is a [key, value] tuple)
21 # * with three, it gets the current hash (as built to this point), the key/index of each value, and then the value
22 #
23 # If the iterable is empty, or no hash was returned from the given block, an empty hash is returned. In the given block, a call to `next()`
24 # will skip that entry, and a call to `break()` will end the iteration.
25 #
26 # @example counting occurrences of strings in an array
27 #   ['a', 'b', 'c', 'c', 'd', 'b'].merge | $hsh, $v | { { $v => $hsh[$v].lest || { 0 } + 1 } } # results in { a => 1, b => 2, c => 2, d => 1 }
28 #
29 # @example skipping values for entries that are longer than 1 char
30 #   ['a', 'b', 'c', 'c', 'd', 'b', 'blah', 'blah'].merge | $hsh, $v | { if $v =~ String[1,1] { { $v => $hsh[$v].lest || { 0 } + 1 } } } # results in { a => 1, b => 2, c => 2, d => 1 }
31 #
32 # The iterative `merge()` has an advantage over doing the same with a general `reduce()` in that the constructed hash
33 # does not have to be copied in each iteration and thus will perform much better with large inputs.
34 Puppet::Functions.create_function(:merge) do
35   # @param args
36   #   Repeated Param - The hashes that are to be merged
37   #
38   # @return
39   #   The merged hash
40   dispatch :merge2hashes do
41     repeated_param 'Variant[Hash, Undef, String[0,0]]', :args # this strange type is backwards compatible
42     return_type 'Hash'
43   end
44
45   # @param args
46   #   Repeated Param - The hashes that are to be merged
47   #
48   # @param block
49   #   A block placed on the repeatable param `args`
50   #
51   # @return
52   #   The merged hash
53   dispatch :merge_iterable3 do
54     repeated_param 'Iterable', :args
55     block_param 'Callable[3,3]', :block
56     return_type 'Hash'
57   end
58
59   # @param args
60   #   Repeated Param - The hashes that are to be merged
61   #
62   # @param block
63   #   A block placed on the repeatable param `args`
64   #
65   # @return
66   #   The merged hash
67   dispatch :merge_iterable2 do
68     repeated_param 'Iterable', :args
69     block_param 'Callable[2,2]', :block
70     return_type 'Hash'
71   end
72
73   def merge2hashes(*hashes)
74     accumulator = {}
75     hashes.each { |h| accumulator.merge!(h) if h.is_a?(Hash) }
76     accumulator
77   end
78
79   def merge_iterable2(iterable)
80     accumulator = {}
81     enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable)
82     enum.each do |v|
83       r = yield(accumulator, v)
84       accumulator.merge!(r) if r.is_a?(Hash)
85     end
86     accumulator
87   end
88
89   def merge_iterable3(iterable)
90     accumulator = {}
91     enum = Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable)
92     if enum.hash_style?
93       enum.each do |entry|
94         r = yield(accumulator, *entry)
95         accumulator.merge!(r) if r.is_a?(Hash)
96       end
97     else
98       begin
99         index = 0
100         loop do
101           r = yield(accumulator, index, enum.next)
102           accumulator.merge!(r) if r.is_a?(Hash)
103           index += 1
104         end
105       rescue StopIteration # rubocop:disable Lint/HandleExceptions
106       end
107     end
108     accumulator
109   end
110 end