Skip to content

Commit

Permalink
Add deep search for suitable filters
Browse files Browse the repository at this point in the history
  • Loading branch information
andershagbard committed Oct 15, 2023
1 parent 0b93182 commit 58d7d1a
Showing 1 changed file with 64 additions and 17 deletions.
81 changes: 64 additions & 17 deletions lib/liquid/standardfilters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,10 @@ def join(input, glue = ' ')
# Sorts the items in an array in case-sensitive alphabetical, or numerical, order.
# @liquid_syntax array | sort
# @liquid_return [array[untyped]]
def sort(input, property = nil)
# @liquid_optional_param deep [boolean | string] Whether to use dot notation to perform a deep search. A string can be passed to change separator.
def sort(input, property = nil, options = {})
options = {} unless options.is_a?(Hash)
deep = deep_search_properties(property, options)
ary = InputIterator.new(input, context)

return [] if ary.empty?
Expand All @@ -363,7 +366,13 @@ def sort(input, property = nil)
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
ary.sort do |a, b|
if deep[:enable]
return nil_safe_compare(a.dig(*deep[:properties]), b.dig(*deep[:properties]))
end

nil_safe_compare(a[property], b[property])
end
rescue TypeError
raise_property_error(property)
end
Expand All @@ -381,7 +390,10 @@ def sort(input, property = nil)
# > string, so sorting on numerical values can lead to unexpected results.
# @liquid_syntax array | sort_natural
# @liquid_return [array[untyped]]
def sort_natural(input, property = nil)
# @liquid_optional_param deep [boolean | string] Whether to use dot notation to perform a deep search. A string can be passed to change separator.
def sort_natural(input, property = nil, options = {})
options = {} unless options.is_a?(Hash)
deep = deep_search_properties(property, options)
ary = InputIterator.new(input, context)

return [] if ary.empty?
Expand All @@ -392,7 +404,13 @@ def sort_natural(input, property = nil)
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
ary.sort do |a, b|
if deep[:enable]
return nil_safe_casecmp(a.dig(*deep[:properties]), b.dig(*deep[:properties]))
end

nil_safe_casecmp(a[property], b[property])
end
rescue TypeError
raise_property_error(property)
end
Expand All @@ -408,7 +426,10 @@ def sort_natural(input, property = nil)
# This requires you to provide both the property name and the associated value.
# @liquid_syntax array | where: string, string
# @liquid_return [array[untyped]]
def where(input, property, target_value = nil)
# @liquid_optional_param deep [boolean | string] Whether to use dot notation to perform a deep search. A string can be passed to change separator.
def where(input, property, target_value = nil, options = {})
options = {} unless options.is_a?(Hash)
deep = deep_search_properties(property, options)
ary = InputIterator.new(input, context)

if ary.empty?
Expand All @@ -424,7 +445,8 @@ def where(input, property, target_value = nil)
end
else
ary.select do |item|
item[property] == target_value
item_value = deep[:enable] ? item.dig(*deep[:properties]) : item[property]
item_value == target_value
rescue TypeError
raise_property_error(property)
rescue NoMethodError
Expand All @@ -441,7 +463,10 @@ def where(input, property, target_value = nil)
# Removes any duplicate items in an array.
# @liquid_syntax array | uniq
# @liquid_return [array[untyped]]
def uniq(input, property = nil)
# @liquid_optional_param deep [boolean | string] Whether to use dot notation to perform a deep search. A string can be passed to change separator.
def uniq(input, property = nil, options = {})
options = {} unless options.is_a?(Hash)
deep = deep_search_properties(property, options)
ary = InputIterator.new(input, context)

if property.nil?
Expand All @@ -450,7 +475,7 @@ def uniq(input, property = nil)
[]
else
ary.uniq do |item|
item[property]
deep[:enable] ? item.dig(*deep[:properties]) : item[property]
rescue TypeError
raise_property_error(property)
rescue NoMethodError
Expand Down Expand Up @@ -479,15 +504,19 @@ def reverse(input)
# Creates an array of values from a specific property of the items in an array.
# @liquid_syntax array | map: string
# @liquid_return [array[untyped]]
def map(input, property)
InputIterator.new(input, context).map do |e|
e = e.call if e.is_a?(Proc)
# @liquid_optional_param deep [boolean | string] Whether to use dot notation to perform a deep search. A string can be passed to change separator.
def map(input, property, options = {})
options = {} unless options.is_a?(Hash)
deep = deep_search_properties(property, options)

InputIterator.new(input, context).map do |item|
item = item.call if item.is_a?(Proc)

if property == "to_liquid"
e
elsif e.respond_to?(:[])
r = e[property]
r.is_a?(Proc) ? r.call : r
item
elsif item.respond_to?(:[])
result = deep[:enable] ? item.dig(*deep[:properties]) : item[property]
result.is_a?(Proc) ? result.call : result
end
end
rescue TypeError
Expand Down Expand Up @@ -876,15 +905,19 @@ def default(input, default_value = '', options = {})
# Returns the sum of all elements in an array.
# @liquid_syntax array | sum
# @liquid_return [number]
def sum(input, property = nil)
# @liquid_optional_param deep [boolean | string] Whether to use dot notation to perform a deep search. A string can be passed to change separator.
def sum(input, property = nil, options = {})
options = {} unless options.is_a?(Hash)
deep = deep_search_properties(property, options)

ary = InputIterator.new(input, context)
return 0 if ary.empty?

values_for_sum = ary.map do |item|
if property.nil?
item
elsif item.respond_to?(:[])
item[property]
deep[:enable] ? item.dig(*deep[:properties]) : item[property]
else
0
end
Expand Down Expand Up @@ -932,6 +965,20 @@ def nil_safe_casecmp(a, b)
end
end

def deep_search_properties(key, options = {})
options = {} unless options.is_a?(Hash)

enable = options['deep'] ? true : false
separator = options['deep'].kind_of?(String) ? options['deep'] : '.' if enable
properties = key.to_s.split(separator) if enable

{
:enable => enable,
:separator => separator,
:properties => properties
}
end

class InputIterator
include Enumerable

Expand Down

0 comments on commit 58d7d1a

Please sign in to comment.