Module: JSON::LD::Compact

Includes:
Utils
Included in:
API
Defined in:
lib/json/ld/compact.rb

Instance Method Summary collapse

Methods included from Utils

#blank_node?, #list?, #subject?, #subject_reference?, #value?

Instance Method Details

#compact(element, property = nil) ⇒ Array, Hash

Compact an expanded Array or Hash given an active property and a context.

Parameters:

  • element (Array, Hash)
  • property (String) (defaults to: nil)

    (nil)

Returns:



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
# File 'lib/json/ld/compact.rb', line 11

def compact(element, property = nil)
  if property.nil?
    debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
  else
    debug("compact") {"property: #{property.inspect}"}
  end
  case element
  when Array
    # 1) If value is an array, process each item in value recursively using
    #    this algorithm, passing copies of the active context and the
    #    active property.
    debug("compact") {"Array #{element.inspect}"}
    result = depth {element.map {|v| compact(v, property)}}
    
    # If element has a single member and the active property has no
    # @container mapping to @list or @set, the compacted value is that
    # member; otherwise the compacted value is element
    if result.length == 1
      debug("=> extract single element: #{result.first.inspect}")
      result.first
    else
      debug("=> array result: #{result.inspect}")
      result
    end
  when Hash
    # 2) Otherwise, if element is an object:
    result = {}
    
    if k = %w(@list @set @value).detect {|container| element.has_key?(container)}
      debug("compact") {"#{k}: container(#{property}) = #{context.container(property)}"}
    end

    k ||= '@id' if element.keys == ['@id']
    
    case k
    when '@value', '@id'
      # If element has an @value property or element is a subject reference, return the result of performing
      # Value Compaction on element using active property.
      v = context.compact_value(property, element, :depth => @depth)
      debug("compact") {"value optimization, return as #{v.inspect}"}
      return v
    when '@list'
      # Otherwise, if the active property has a @container mapping to @list and element has a corresponding @list property, recursively compact that property's value passing a copy of the active context and the active property ensuring that the result is an array and removing null values.
      compacted_key = context.compact_iri(k, :position => :predicate, :depth => @depth)
      v = depth { compact(element[k], property) }
      
      # Return either the result as an array, as an object with a key of @list (or appropriate alias from active context
      v = [v].compact unless v.is_a?(Array)
      v = {compacted_key => v} unless context.container(property) == k
      debug("compact") {"@list result, return as #{v.inspect}"}
      return v
    end

    # Otherwise, for each property and value in element:
    element.each do |key, value|
      debug("compact") {"#{key}: #{value.inspect}"}

      if %(@id @type).include?(key)
        compacted_key = context.compact_iri(key, :position => :predicate, :depth => @depth)

        result[compacted_key] = case value
        when String
          # If value is a string, the compacted value is the result of performing IRI Compaction on value.
          debug {" => compacted string for #{key}"}
          context.compact_iri(value, :position => :subject, :depth => @depth)
        when Array
          # Otherwise, value must be an array. Perform IRI Compaction on every entry of value. If value contains just one entry, value is set to that entry
          compacted_value = value.map {|v| context.compact_iri(v, :position => :subject, :depth => @depth)}
          debug {" => compacted value(#{key}): #{compacted_value.inspect}"}
          compacted_value = compacted_value.first if compacted_value.length == 1
          compacted_value
        end
      else
        if value.empty?
          # Make sure that an empty array is preserved
          compacted_key = context.compact_iri(key, :position => :predicate, :depth => @depth)
          next if compacted_key.nil?
          result[compacted_key] = value
        end

        # For each item in value:
        raise ProcessingError, "found #{value.inspect} for #{key} if #{element.inspect}" unless value.is_a?(Array)
        value.each do |item|
          compacted_key = context.compact_iri(key, :position => :predicate, :value => item, :depth => @depth)
          debug {" => compacted key: #{compacted_key.inspect} for #{item.inspect}"}
          next if compacted_key.nil?

          compacted_item = depth {self.compact(item, compacted_key)}
          debug {" => compacted value: #{compacted_value.inspect}"}
        
          case result[compacted_key]
          when Array
            result[compacted_key] << compacted_item
          when nil
            if !compacted_value.is_a?(Array) && context.container(compacted_key) == '@set'
              compacted_item = [compacted_item].compact
              debug {" => as @set: #{compacted_item.inspect}"}
            end
            result[compacted_key] = compacted_item
          else
            result[compacted_key] = [result[compacted_key], compacted_item]
          end
        end
      end
    end
    
    # Re-order result keys
    r = Hash.ordered
    result.keys.sort.each {|k| r[k] = result[k]}
    r
  else
    # For other types, the compacted value is the element value
    debug("compact") {element.class.to_s}
    element
  end
end