Class: Alf::Types::Ordering

Inherits:
Object
  • Object
show all
Defined in:
lib/alf/types/ordering.rb

Overview

Defines an ordering on tuple attributes

Constant Summary collapse

ArrayOfArray =
->(x){
  x.is_a?(Array) && x.all?{|y| y.is_a?(Array)}
}
ArrayWithDirections =
->(x){
  x.each_with_index.all?{|elm,i| (i%2==0) || elm.to_s =~ /^asc|desc$/ }
}
EMPTY =
Ordering.new([])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ordering = []) ⇒ Ordering

Creates an ordering instance


40
41
42
43
# File 'lib/alf/types/ordering.rb', line 40

def initialize(ordering = [])
  super
  @sorter = lambda{|t1,t2| compare(t1, t2) }
end

Instance Attribute Details

#sorterProc (readonly)


35
36
37
# File 'lib/alf/types/ordering.rb', line 35

def sorter
  @sorter
end

Class Method Details

.new(array) ⇒ Object


9
10
11
# File 'lib/alf/types/ordering.rb', line 9

def self.new(array)
  super(array.map{|(x,y)| [Selector.coerce(x), y.to_sym] })
end

Instance Method Details

#<=(other) ⇒ Object

Check if this ordering subsumes another one.


154
155
156
157
# File 'lib/alf/types/ordering.rb', line 154

def <=(other)
  size = reused_instance.size
  reused_instance == other.to_a[0...size]
end

#[](attribute) ⇒ Symbol

Returns the directions associated with an attribute name, nil if no such attribute.


52
53
54
55
56
# File 'lib/alf/types/ordering.rb', line 52

def [](attribute)
  attribute = Selector.coerce(attribute)
  pair = reused_instance.find{|p| p.first == attribute }
  pair && pair.last
end

#compare(t1, t2) ⇒ -1, 0 or 1

Compares two tuples according to this ordering.

Both t1 and t2 should have all attributes used by this ordering. Strange results may appear if it's not the case.


67
68
69
70
71
72
73
74
75
76
# File 'lib/alf/types/ordering.rb', line 67

def compare(t1, t2)
  extract = proc{|t,x| Array(x).inject(t){|m,a| m[a]} }
  reused_instance.each do |atr, dir|
    x, y = extract[t1,atr], extract[t2,atr]
    comp = Support.robust_compare(x, y)
    comp *= -1 if dir == :desc
    return comp unless comp == 0
  end
  return 0
end

#dive(attr) ⇒ Ordering

Dives into a relation/tuple valued attribute `attr`.


139
140
141
142
143
144
# File 'lib/alf/types/ordering.rb', line 139

def dive(attr)
  attrs = reused_instance
        .map   {|(s,d)| [s.dive(attr), d] }
        .reject{|(s,d)| s.nil? }
  Ordering.new(attrs)
end

#merge(other, &bl) ⇒ Ordering Also known as: +

Computes the union of this ordering with another one.

The union is simply defined by extension of self with other's attributes and directions. Duplicates are automatically removed.

When a conflict arises (same attribute but not same direction), the block is yield with the attribute name, then `self`'s and `other`'s directions as arguments. The block is expected to return the direction to use to the attribute. A default block is provided that always favors the direction found in `other`.


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/alf/types/ordering.rb', line 91

def merge(other, &bl)
  bl ||= lambda{|attr,d1,d2| d2 }
  other = Ordering.coerce(other)
  attributes = self.selectors | other.selectors
  directions = attributes.to_a.map{|a|
    left, right = self[a], other[a]
    direction = if left.nil? or right.nil?
                  left || right
                elsif left == right
                  left
                else
                  bl.call(a, left, right)
                end
    [a, direction]
  }
  Ordering.new(directions)
end

#reverseOrdering

Reverse this ordering.


114
115
116
117
118
# File 'lib/alf/types/ordering.rb', line 114

def reverse
  Ordering.new(reused_instance.map{|attr,dir|
    [ attr, dir == :asc ? :desc : :asc ]
  })
end

#selectorsArray[Selector]

Returns the list of selectors


149
150
151
# File 'lib/alf/types/ordering.rb', line 149

def selectors
  reused_instance.map(&:first)
end

#to_attr_listAttrList

Converts to an attribute list.


163
164
165
# File 'lib/alf/types/ordering.rb', line 163

def to_attr_list
  AttrList.new(selectors.map{|x| Array(x).first })
end

#to_lispyString

Returns a lispy expression.


170
171
172
# File 'lib/alf/types/ordering.rb', line 170

def to_lispy
  Support.to_ruby_literal(to_a.map{|(x,d)| [x.outcoerce, d]})
end

#to_ruby_literalString Also known as: to_s, inspect

Returns a ruby literal for this ordering.


177
178
179
# File 'lib/alf/types/ordering.rb', line 177

def to_ruby_literal
  "Alf::Ordering[#{Support.to_ruby_literal(reused_instance)}]"
end

#total(keys, &bl) ⇒ Ordering

Returns a total ordering given some key definitions.


123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/alf/types/ordering.rb', line 123

def total(keys, &bl)
  list = to_attr_list
  if k = keys.to_a.find{|k| k.to_attr_list.subset_of?(list) }
    self
  elsif k = keys.first
    merge(k.to_ordering){|k,d1,d2| d1 }
  elsif bl && (key = bl.call.to_attr_list)
    merge(key.to_ordering){|k,d1,d2| d1 }
  else
    raise NotSupportedError, "Unable to find a total order (no key available)"
  end
end