Class: Multimap

Inherits:
Hash
  • Object
show all
Defined in:
lib/multimap.rb

Overview

Multimap is a generalization of a map or associative array abstract data type in which more than one value may be associated with and returned for a given key.

Direct Known Subclasses

NestedMultimap

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(default = []) ⇒ Multimap

call-seq:

Multimap.new           => multimap
Multimap.new(default)  => multimap

Returns a new, empty multimap.

map = Multimap.new(Set.new)
h["a"] = 100
h["b"] = 200
h["a"]           #=> [100].to_set
h["c"]           #=> [].to_set


72
73
74
# File 'lib/multimap.rb', line 72

def initialize(default = [])
  super
end

Class Method Details

.[](*args) ⇒ Object

call-seq:

Multimap[ [key =>|, value]* ]   => multimap

Creates a new multimap populated with the given objects.

Multimap["a", 100, "b", 200]       #=> {"a"=>[100], "b"=>[200]}
Multimap["a" => 100, "b" => 200]   #=> {"a"=>[100], "b"=>[200]}


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
# File 'lib/multimap.rb', line 28

def self.[](*args)
  default = []

  if args.size == 2 && args.last.is_a?(Hash)
    default = args.shift
  elsif !args.first.is_a?(Hash) && args.size % 2 == 1
    default = args.shift
  end

  if args.size == 1 && args.first.is_a?(Hash)
    args[0] = args.first.inject({}) { |hash, (key, value)|
      unless value.is_a?(default.class)
        value = (default.dup << value)
      end
      hash[key] = value
      hash
    }
  else
    index = 0
    args.map! { |value|
      unless index % 2 == 0 || value.is_a?(default.class)
        value = (default.dup << value)
      end
      index += 1
      value
    }
  end

  map = super
  map.default = default
  map
end

Instance Method Details

#containersObject

call-seq:

map.containers    => array

Returns a new array populated with the containers from map. See also Multimap#keys and Multimap#values.

map = Multimap["a" => 100, "b" => [200, 300]]
map.containers   #=> [[100], [200, 300]]


439
440
441
442
443
# File 'lib/multimap.rb', line 439

def containers
  containers = []
  each_container { |container| containers << container }
  containers
end

#delete(key, value = nil) ⇒ Object

call-seq:

map.delete(key, value)  => value
map.delete(key)         => value

Deletes and returns a key-value pair from map. If only key is given, all the values matching that key will be deleted.

map = Multimap["a" => 100, "b" => [200, 300]]
map.delete("b", 300) #=> 300
map.delete("a")      #=> [100]


119
120
121
122
123
124
125
# File 'lib/multimap.rb', line 119

def delete(key, value = nil)
  if value
    hash_aref(key).delete(value)
  else
    super(key)
  end
end

#eachObject

call-seq:

map.each { |key, value| block } => map

Calls block for each key/value pair in map, passing the key and value to the block as a two-element array.

map = Multimap["a" => 100, "b" => [200, 300]]
map.each { |key, value| puts "#{key} is #{value}" }

produces:

a is 100
b is 200
b is 300


141
142
143
144
145
# File 'lib/multimap.rb', line 141

def each
  each_pair do |key, value|
    yield [key, value]
  end
end

#each_associationObject

call-seq:

map.each_association { |key, container| block } => map

Calls block once for each key/container in map, passing the key and container to the block as parameters.

map = Multimap["a" => 100, "b" => [200, 300]]
map.each_association { |key, container| puts "#{key} is #{container}" }

produces:

a is [100]
b is [200, 300]


160
161
162
# File 'lib/multimap.rb', line 160

def each_association
  # each_pair
end

#each_containerObject

call-seq:

map.each_container { |container| block } => map

Calls block for each container in map, passing the container as a parameter.

map = Multimap["a" => 100, "b" => [200, 300]]
map.each_container { |container| puts container }

produces:

[100]
[200, 300]


182
183
184
185
186
# File 'lib/multimap.rb', line 182

def each_container
  each_association do |_, container|
    yield container
  end
end

#each_keyObject

call-seq:

map.each_key { |key| block } => map

Calls block for each key in hsh, passing the key as a parameter.

map = Multimap["a" => 100, "b" => [200, 300]]
map.each_key { |key| puts key }

produces:

a
b
b


202
203
204
205
206
# File 'lib/multimap.rb', line 202

def each_key
  each_pair do |key, _|
    yield key
  end
end

#each_pairObject

call-seq:

map.each_pair { |key_value_array| block } => map

Calls block for each key/value pair in map, passing the key and value as parameters.

map = Multimap["a" => 100, "b" => [200, 300]]
map.each_pair { |key, value| puts "#{key} is #{value}" }

produces:

a is 100
b is 200
b is 300


222
223
224
225
226
227
228
# File 'lib/multimap.rb', line 222

def each_pair
  each_association do |key, values|
    values.each do |value|
      yield key, value
    end
  end
end

#each_valueObject

call-seq:

map.each_value { |value| block } => map

Calls block for each key in map, passing the value as a parameter.

map = Multimap["a" => 100, "b" => [200, 300]]
map.each_value { |value| puts value }

produces:

100
200
300


244
245
246
247
248
# File 'lib/multimap.rb', line 244

def each_value
  each_pair do |_, value|
    yield value
  end
end

#freezeObject

:nodoc:



250
251
252
253
254
# File 'lib/multimap.rb', line 250

def freeze #:nodoc:
  each_container { |container| container.freeze }
  default.freeze
  super
end

#has_value?(value) ⇒ Boolean Also known as: value?

call-seq:

map.has_value?(value)    => true or false
map.value?(value)        => true or false

Returns true if the given value is present for any key in map.

map = Multimap["a" => 100, "b" => [200, 300]]
map.has_value?(300)   #=> true
map.has_value?(999)   #=> false

Returns:

  • (Boolean)


266
267
268
# File 'lib/multimap.rb', line 266

def has_value?(value)
  values.include?(value)
end

#index(value) ⇒ Object

call-seq:

map.index(value)    => key

Returns the key for a given value. If not found, returns nil.

map = Multimap["a" => 100, "b" => [200, 300]]
map.index(100)   #=> "a"
map.index(200)   #=> "b"
map.index(999)   #=> nil


281
282
283
# File 'lib/multimap.rb', line 281

def index(value)
  invert[value]
end

#initialize_copy(original) ⇒ Object

:nodoc:



76
77
78
79
80
81
# File 'lib/multimap.rb', line 76

def initialize_copy(original) #:nodoc:
  super
  clear
  original.each_pair { |key, container| self[key] = container }
  self.default = original.default.dup
end

#invertObject

call-seq:

map.invert => multimap

Returns a new multimap created by using map’s values as keys, and the keys as values.

map = Multimap["n" => 100, "m" => 100, "d" => [200, 300]]
map.invert #=> Multimap[100 => ["n", "m"], 200 => "d", 300 => "d"]


315
316
317
318
319
# File 'lib/multimap.rb', line 315

def invert
  h = self.class.new(default.dup)
  each_pair { |key, value| h[value] = key }
  h
end

#keysObject

call-seq:

map.keys    => multiset

Returns a new Multiset populated with the keys from this hash. See also Multimap#values and Multimap#containers.

map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
map.keys   #=> Multiset.new(["a", "b", "b", "c"])


329
330
331
332
333
# File 'lib/multimap.rb', line 329

def keys
  keys = Multiset.new
  each_key { |key| keys << key }
  keys
end

#merge(other) ⇒ Object

call-seq:

map.merge(other_map) => multimap

Returns a new multimap containing the contents of other_map and the contents of map.

map1 = Multimap["a" => 100, "b" => 200]
map2 = Multimap["a" => 254, "c" => 300]
map2.merge(map2) #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
map1             #=> Multimap["a" => 100, "b" => 200]


360
361
362
# File 'lib/multimap.rb', line 360

def merge(other)
  dup.update(other)
end

#replace(other) ⇒ Object

call-seq:

map.replace(other_map) => map

Replaces the contents of map with the contents of other_map.

map = Multimap["a" => 100, "b" => 200]
map.replace({ "c" => 300, "d" => 400 })
#=> Multimap["c" => 300, "d" => 400]


294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/multimap.rb', line 294

def replace(other)
  case other
  when Array
    super(self.class[self.default, *other])
  when Hash
    super(self.class[self.default, other])
  when self.class
    super
  else
    raise ArgumentError
  end
end

#selectObject

call-seq:

map.select { |key, value| block }   => multimap

Returns a new Multimap consisting of the pairs for which the block returns true.

map = Multimap["a" => 100, "b" => 200, "c" => 300]
map.select { |k,v| k > "a" }  #=> Multimap["b" => 200, "c" => 300]
map.select { |k,v| v < 200 }  #=> Multimap["a" => 100]


397
398
399
400
401
402
# File 'lib/multimap.rb', line 397

def select
  inject(self.class.new) { |map, (key, value)|
    map[key] = value if yield([key, value])
    map
  }
end

#sizeObject Also known as: length

call-seq:

map.length    =>  fixnum
map.size      =>  fixnum

Returns the number of key-value pairs in the map.

map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
map.length        #=> 4
map.delete("a")   #=> 100
map.length        #=> 3


345
346
347
# File 'lib/multimap.rb', line 345

def size
  values.size
end

#store(key, value) ⇒ Object Also known as: []=

call-seq:

map[key] = value        => value
map.store(key, value)   => value

Associates the value given by value with the key given by key. Unlike a regular hash, multiple can be assoicated with the same value.

map = Multimap["a" => 100, "b" => 200]
map["a"] = 9
map["c"] = 4
map   #=> {"a" => [100, 9], "b" => [200], "c" => [4]}


100
101
102
103
104
105
# File 'lib/multimap.rb', line 100

def store(key, value)
  update_container(key) do |container|
    container << value
    container
  end
end

#to_aObject

call-seq:

map.to_a => array

Converts map to a nested array of [key, value] arrays.

map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
map.to_a   #=> [["a", 100], ["b", 200], ["b", 300], ["c", 400]]


412
413
414
415
416
417
418
# File 'lib/multimap.rb', line 412

def to_a
  ary = []
  each_pair do |key, value|
    ary << [key, value]
  end
  ary
end

#to_hashObject

call-seq:

map.to_hash => hash

Converts map to a basic hash.

map = Multimap["a" => 100, "b" => [200, 300]]
map.to_hash   #=> { "a" => [100], "b" => [200, 300] }


427
428
429
# File 'lib/multimap.rb', line 427

def to_hash
  dup
end

#update(other) ⇒ Object Also known as: merge!

call-seq:

map.merge!(other_map)    => multimap
map.update(other_map)    => multimap

Adds each pair from other_map to map.

map1 = Multimap["a" => 100, "b" => 200]
map2 = Multimap["b" => 254, "c" => 300]

map1.merge!(map2)
#=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]


375
376
377
378
379
380
381
382
383
384
385
# File 'lib/multimap.rb', line 375

def update(other)
  case other
  when self.class
    other.each_pair { |key, value| store(key, value) }
  when Hash
    update(self.class[self.default, other])
  else
    raise ArgumentError
  end
  self
end

#valuesObject

call-seq:

map.values    => array

Returns a new array populated with the values from map. See also Multimap#keys and Multimap#containers.

map = Multimap["a" => 100, "b" => [200, 300]]
map.values   #=> [100, 200, 300]


453
454
455
456
457
# File 'lib/multimap.rb', line 453

def values
  values = []
  each_value { |value| values << value }
  values
end