Class: Hashery::Dictionary

Inherits:
Object show all
Includes:
Enumerable
Defined in:
lib/hashery/dictionary.rb

Overview

The Dictionary class is a Hash that preserves order. So it has some array-like extensions also. By defualt a Dictionary object preserves insertion order, but any order can be specified including alphabetical key order.

Using a Dictionary is almost the same as using a Hash.

# You can do simply
hsh = Dictionary.new
hsh['z'] = 1
hsh['a'] = 2
hsh['c'] = 3
p hsh.keys     #=> ['z','a','c']

# or using Dictionary[] method
hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
p hsh.keys     #=> ['z','a','c']

# but this don't preserve order
hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
p hsh.keys     #=> ['a','c','z']

# Dictionary has useful extensions: push, pop and unshift
p hsh.push('to_end', 15)       #=> true, key added
p hsh.push('to_end', 30)       #=> false, already - nothing happen
p hsh.unshift('to_begin', 50)  #=> true, key added
p hsh.unshift('to_begin', 60)  #=> false, already - nothing happen
p hsh.keys                     #=> ["to_begin", "a", "c", "z", "to_end"]
p hsh.pop                      #=> ["to_end", 15], if nothing remains, return nil
p hsh.keys                     #=> ["to_begin", "a", "c", "z"]
p hsh.shift                    #=> ["to_begin", 30], if nothing remains, return nil

Notes

  • You can use #order_by to set internal sort order.

  • #<< takes a two element [k,v] array and inserts.

  • Use ::auto which creates Dictionay sub-entries as needed.

  • And ::alpha which creates a new Dictionary sorted by key.

Acknowledgments

Dictionary is a port of OrderHash 2.0 Copyright © 2005 Jan Molic.

People who have contributed to this class since then include:

  • Andrew Johnson (merge, to_a, inspect, shift and Hash[])

  • Jeff Sharpe (reverse and reverse!)

  • Thomas Leitner (has_key? and key?)

OrderedHash is public domain.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args, &blk) ⇒ Dictionary

New Dictiionary.



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/hashery/dictionary.rb', line 128

def initialize(*args, &blk)
  @order = []
  @order_by = nil
  if blk
    dict = self                                  # This ensures automatic key entry effect the
    oblk = lambda{ |hsh, key| blk[dict,key] }    # dictionary rather then just the interal hash.
    @hash = Hash.new(*args, &oblk)
  else
    @hash = Hash.new(*args)
  end
end

Class Method Details

.[](*args) ⇒ Dictionary

TODO:

Is this needed? Doesn’t the super class do this?

Create a new Dictionary storing argument pairs as an initial mapping.

Returns:



67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/hashery/dictionary.rb', line 67

def [](*args)
  hsh = new
  if Hash === args[0]
    hsh.replace(args[0])
  elsif (args.size % 2) != 0
    raise ArgumentError, "odd number of elements for Hash"
  else
    while !args.empty?
      hsh[args.shift] = args.shift
    end
  end
  hsh
end

.alphabetic(*args, &block) ⇒ Object Also known as: alpha

Alternate to #new which creates a dictionary sorted by the key as a string.

d = Dictionary.alphabetic
d["z"] = 1
d["y"] = 2
d["x"] = 3
d  #=> {"x"=>3,"y"=>2,"z"=>2}

This is equivalent to:

Dictionary.new.order_by { |key,value| key.to_s }


103
104
105
# File 'lib/hashery/dictionary.rb', line 103

def alphabetic(*args, &block)
  new(*args, &block).order_by { |key,value| key.to_s }
end

.auto(*args) ⇒ Object

Alternate to #new which auto-creates sub-dictionaries as needed.

Examples:

d = Dictionary.auto
d["a"]["b"]["c"] = "abc"  #=> { "a"=>{"b"=>{"c"=>"abc"}}}


118
119
120
121
122
# File 'lib/hashery/dictionary.rb', line 118

def auto(*args)
  #AutoDictionary.new(*args)
  leet = lambda { |hsh, key| hsh[key] = new(&leet) }
  new(*args, &leet)
end

.new_by(*args, &blk) ⇒ Object

Like #new but the block sets the order instead of the default.

Dictionary.new_by{ |k,v| k }



86
87
88
# File 'lib/hashery/dictionary.rb', line 86

def new_by(*args, &blk)
  new(*args).order_by(&blk)
end

Instance Method Details

#<<(kv) ⇒ Object

Same as #push.



441
442
443
# File 'lib/hashery/dictionary.rb', line 441

def <<(kv)
  push(*kv)
end

#==(hsh2) ⇒ Object

Is the dictionary instance equivalent to another?



232
233
234
235
236
237
238
239
# File 'lib/hashery/dictionary.rb', line 232

def ==(hsh2)
  if hsh2.is_a?( Dictionary )
    @order == hsh2.order &&
    @hash  == hsh2.instance_variable_get("@hash")
  else
    false
  end
end

#[](key) ⇒ Object

Lookup entry with key.



244
245
246
# File 'lib/hashery/dictionary.rb', line 244

def [] key
  @hash[ key ]
end

#[]=(k, i = nil, v = nil) ⇒ Object

Store operator.

h[key] = value

Or with additional index.

h[key,index] = value


264
265
266
267
268
269
270
# File 'lib/hashery/dictionary.rb', line 264

def []=(k, i=nil, v=nil)
  if v
    insert(i,k,v)
  else
    store(k,i)
  end
end

#clearObject

Clear dictionary of all entries.



299
300
301
302
# File 'lib/hashery/dictionary.rb', line 299

def clear
  @order = []
  @hash.clear
end

#delete(key) ⇒ Object

Delete the entry with given key.



307
308
309
310
# File 'lib/hashery/dictionary.rb', line 307

def delete(key)
  @order.delete(key)
  @hash.delete(key)
end

#delete_ifObject

Delete entry if it fits conditional block.



341
342
343
344
# File 'lib/hashery/dictionary.rb', line 341

def delete_if
  order.clone.each { |k| delete k if yield(k,@hash[k]) }
  self
end

#dupDictionary

Duplicate dictionary.

Returns:



482
483
484
485
486
# File 'lib/hashery/dictionary.rb', line 482

def dup
  a = []
  each{ |k,v| a << k; a << v }
  self.class[*a]
end

#eachObject Also known as: each_pair

Iterate over each key-value pair.



331
332
333
334
# File 'lib/hashery/dictionary.rb', line 331

def each
  order.each { |k| yield( k,@hash[k] ) }
  self
end

#each_keyObject

Iterate over each key.



315
316
317
318
# File 'lib/hashery/dictionary.rb', line 315

def each_key
  order.each { |k| yield( k ) }
  self
end

#each_valueObject

Iterate over each value.



323
324
325
326
# File 'lib/hashery/dictionary.rb', line 323

def each_value
  order.each { |k| yield( @hash[k] ) }
  self
end

#empty?Boolean

Is the dictionary empty?

Returns:

  • (Boolean)

    ‘true` or `false`.



570
571
572
# File 'lib/hashery/dictionary.rb', line 570

def empty?
  @hash.empty?
end

#fetch(key, *a, &b) ⇒ Object

Featch entry given key.



251
252
253
# File 'lib/hashery/dictionary.rb', line 251

def fetch(key, *a, &b)
  @hash.fetch(key, *a, &b)
end

#first(x = nil) ⇒ Object

Get/set initial entry value.



543
544
545
546
# File 'lib/hashery/dictionary.rb', line 543

def first(x=nil)
  return @hash[order.first] unless x
  order.first(x).collect { |k| @hash[k] }
end

#has_key?(key) ⇒ Boolean

Does the dictionary have a given key.

Returns:

  • (Boolean)

    ‘true` or `false`.



579
580
581
# File 'lib/hashery/dictionary.rb', line 579

def has_key?(key)
  @hash.has_key?(key)
end

#hash_tableObject (protected)

Underlying hash table.



635
636
637
# File 'lib/hashery/dictionary.rb', line 635

def hash_table
  @hash
end

#insert(index, key, value) ⇒ Object

Insert entry into dictionary at specific index position.

Parameters:

  • index (Integer)

    Position of order placement.

  • key (Object)

    Key to associate with value.

  • value (Object)

    Value to associate with key.

Returns:

  • ‘value` stored.



281
282
283
284
# File 'lib/hashery/dictionary.rb', line 281

def insert(index, key, value)
  @order.insert(index, key)
  @hash.store(key, value)
end

#inspectString

Inspection string for Dictionary.

Returns:

  • (String)

    Returns String.



471
472
473
474
475
# File 'lib/hashery/dictionary.rb', line 471

def inspect
  ary = []
  each {|k,v| ary << k.inspect + "=>" + v.inspect}
  '{' + ary.join(", ") + '}'
end

#invertDictionary

Invert the dictionary.

Returns:

  • (Dictionary)

    Returns Dictionary New dictionary that is inverse of the original.



371
372
373
374
375
# File 'lib/hashery/dictionary.rb', line 371

def invert
  hsh2 = self.class.new
  order.each { |k| hsh2[@hash[k]] = k }
  hsh2
end

#key?(key) ⇒ Boolean

Does the dictionary have a given key.

Returns:

  • (Boolean)

    ‘true` or `false`.



588
589
590
# File 'lib/hashery/dictionary.rb', line 588

def key?(key)
  @hash.key?(key)
end

#keysArray

List of all dictionary keys.

Returns:

  • (Array)

    Returns Array.



362
363
364
# File 'lib/hashery/dictionary.rb', line 362

def keys
  order
end

#last(x = nil) ⇒ Object

Get/set last entry value.



551
552
553
554
# File 'lib/hashery/dictionary.rb', line 551

def last(x=nil)
  return @hash[order.last] unless x
  order.last(x).collect { |k| @hash[k] }
end

#lengthObject Also known as: size

Number of items in the dictionary.



559
560
561
# File 'lib/hashery/dictionary.rb', line 559

def length
  @order.length
end

#merge(hsh2) ⇒ Dictionary

Merge other hash creating new dictionary.

Returns:



506
507
508
# File 'lib/hashery/dictionary.rb', line 506

def merge(hsh2)
  self.dup.update(hsh2)
end

#orderArray

Order of keys.

Returns:

  • (Array)

    Returns Array.



145
146
147
148
# File 'lib/hashery/dictionary.rb', line 145

def order
  reorder if @order_by
  @order
end

#order_by(&block) ⇒ Object

Keep dictionary sorted by a specific sort order.

Parameters:

  • block

    Ordering procedure.

Returns:

  • self.



157
158
159
160
161
# File 'lib/hashery/dictionary.rb', line 157

def order_by( &block )
  @order_by = block
  order
  self
end

#order_by_keyObject

Keep dictionary sorted by key.

d = Dictionary.new.order_by_key
d["z"] = 1
d["y"] = 2
d["x"] = 3
d  #=> {"x"=>3,"y"=>2,"z"=>2}

This is equivalent to:

Dictionary.new.order_by { |key,value| key }

The initializer Dictionary#alpha also provides this.

Returns:

  • self.



180
181
182
183
184
185
186
187
188
# File 'lib/hashery/dictionary.rb', line 180

def order_by_key
  if block_given?
    @order_by = Proc.new{ |k,v| yield(k) }
  else
    @order_by = Proc.new{ |k,v| k }
  end
  order
  self
end

#order_by_valueObject

Keep dictionary sorted by value.

d = Dictionary.new.order_by_value
d["z"] = 1
d["y"] = 2
d["x"] = 3
d  #=> {"x"=>3,"y"=>2,"z"=>2}

This is equivalent to:

Dictionary.new.order_by { |key,value| value }


203
204
205
206
207
208
209
210
211
# File 'lib/hashery/dictionary.rb', line 203

def order_by_value
  if block_given?
    @order_by = Proc.new{ |k,v| yield(v) }
  else
    @order_by = Proc.new{ |k,v| v }
  end
  order
  self
end

#popObject

Pop entry off the bottom of dictionary.



461
462
463
464
# File 'lib/hashery/dictionary.rb', line 461

def pop
  key = order.last
  key ? [key,delete(key)] : nil
end

#push(k, v) ⇒ Object

Push entry on to bottom of the dictionary.



448
449
450
451
452
453
454
455
456
# File 'lib/hashery/dictionary.rb', line 448

def push(k,v)
  unless @hash.include?( k )
    @order.push( k )
    @hash.store( k,v )
    true
  else
    false
  end
end

#reject(&block) ⇒ Dictionary

Reject entries based on give condition block and return new dictionary.

Returns:



383
384
385
# File 'lib/hashery/dictionary.rb', line 383

def reject(&block)
  self.dup.delete_if(&block)
end

#reject!(&block) ⇒ Hash

Reject entries based on give condition block.

Returns:

  • (Hash)

    Returns Hash of rejected entries.



394
395
396
397
# File 'lib/hashery/dictionary.rb', line 394

def reject!( &block )
  hsh2 = reject(&block)
  self == hsh2 ? nil : hsh2
end

#reorderObject

Re-apply the sorting procedure.



216
217
218
219
220
221
222
# File 'lib/hashery/dictionary.rb', line 216

def reorder
  if @order_by
    assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
    @order = assoc.collect{ |k,v| k }
  end
  @order
end

#replace(hsh2) ⇒ Object

Replace dictionary entries with new table.



402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/hashery/dictionary.rb', line 402

def replace(hsh2)
  case hsh2
  when Dictionary
    @order = hsh2.order
    @hash  = hsh2.to_h
  when Hash
    @hash  = hsh2
    @order = @hash.keys
  else
    @hash  = hsh2.to_h
    @order = @hash.keys
  end
  reorder
end

#reverseDictionary

Reverse the order of duplicte dictionary.

Returns:



536
537
538
# File 'lib/hashery/dictionary.rb', line 536

def reverse
  dup.reverse!
end

#reverse!Object

Reverse the order of the dictionary.

Returns:

  • self.



526
527
528
529
# File 'lib/hashery/dictionary.rb', line 526

def reverse!
  @order.reverse!
  self
end

#selectArray

Select items from dictiornary.

Returns:

  • (Array)

    Returns Array of two-element arrays.



515
516
517
518
519
# File 'lib/hashery/dictionary.rb', line 515

def select
  ary = []
  each { |k,v| ary << [k,v] if yield k,v }
  ary
end

#shiftObject

Remove entry from the to top of dictionary.



420
421
422
423
# File 'lib/hashery/dictionary.rb', line 420

def shift
  key = order.first
  key ? [key,delete(key)] : super
end

#store(key, value) ⇒ Object

Add entry into dictionary.

Returns:

  • ‘value`.



291
292
293
294
# File 'lib/hashery/dictionary.rb', line 291

def store(key, value)
  @order.push(key) unless @hash.has_key?(key)
  @hash.store(key, value)
end

#to_aArray

Convert to array.

Returns:

  • (Array)

    Returns Array of two-element arrays.



597
598
599
600
601
# File 'lib/hashery/dictionary.rb', line 597

def to_a
  ary = []
  each { |k,v| ary << [k,v] }
  ary
end

#to_hHash

Get a duplicate of the underlying hash table.

Returns:

  • (Hash)

    Returns Hash.



626
627
628
# File 'lib/hashery/dictionary.rb', line 626

def to_h
  @hash.dup
end

#to_hashHash

Get a duplicate of the underlying hash table.

Returns:

  • (Hash)

    Returns Hash.



617
618
619
# File 'lib/hashery/dictionary.rb', line 617

def to_hash
  @hash.dup
end

#to_sString

Convert to array then to string.

Returns:

  • (String)

    Returns String.



608
609
610
# File 'lib/hashery/dictionary.rb', line 608

def to_s
  self.to_a.to_s
end

#unshift(k, v) ⇒ Object

Push entry on to the top of dictionary.



428
429
430
431
432
433
434
435
436
# File 'lib/hashery/dictionary.rb', line 428

def unshift( k,v )
  unless @hash.include?( k )
    @order.unshift( k )
    @hash.store( k,v )
    true
  else
    false
  end
end

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

Update dictionary with other hash.

Returns:

  • self.



493
494
495
496
497
# File 'lib/hashery/dictionary.rb', line 493

def update( hsh2 )
  hsh2.each { |k,v| self[k] = v }
  reorder
  self
end

#valuesArray

List of all dictionary values.

Returns:

  • (Array)

    Returns Array.



351
352
353
354
355
# File 'lib/hashery/dictionary.rb', line 351

def values
  ary = []
  order.each { |k| ary.push @hash[k] }
  ary
end