From af66bc8a5f9ae29fa8121c418675d43f3f755a03 Mon Sep 17 00:00:00 2001 From: Adam Klingbaum Date: Thu, 15 Jun 2023 18:57:57 +0000 Subject: [PATCH] Add `sum` filter to standard filters --- lib/liquid/standardfilters.rb | 25 +++++++++++ test/integration/standard_filter_test.rb | 54 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 53662fef..aee43034 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -869,6 +869,31 @@ module Liquid false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input end + # @liquid_public_docs + # @liquid_type filter + # @liquid_category array + # @liquid_summary + # Returns the sum of all elements in an array. + # @liquid_syntax array | sum + # @liquid_return [number] + def sum(input, property = nil) + ary = InputIterator.new(input, context) + + if ary.empty? + 0 + elsif property.nil? + ary.sum do |item| + Utils.to_number(item) + end + else + ary.sum do |item| + item.respond_to?(:[]) ? Utils.to_number(item[property]) : 0 + rescue TypeError + raise_property_error(property) + end + end + end + private attr_reader :context diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index bc8c6a4a..dd15ff4e 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -928,6 +928,60 @@ class StandardFiltersTest < Minitest::Test assert_equal([{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")) end + def test_sum_with_all_numbers + input = [1, 2] + + assert_equal(3, @filters.sum(input)) + assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do + @filters.sum(input, "quantity") + end + end + + def test_sum_with_numeric_strings + input = [1, 2, "3", "4"] + + assert_equal(10, @filters.sum(input)) + assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do + @filters.sum(input, "quantity") + end + end + + def test_sum_with_nested_arrays + input = [1, [2, [3, 4]]] + + assert_equal(10, @filters.sum(input)) + assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do + @filters.sum(input, "quantity") + end + end + + def test_sum_with_indexable_map_values + input = [{ "quantity" => 1 }, { "quantity" => 2, "weight" => 3 }, { "weight" => 4 }] + + assert_equal(0, @filters.sum(input)) + assert_equal(3, @filters.sum(input, "quantity")) + assert_equal(7, @filters.sum(input, "weight")) + assert_equal(0, @filters.sum(input, "subtotal")) + end + + def test_sum_with_indexable_non_map_values + input = [1, [2], "foo", { "quantity" => 3 }] + + assert_equal(3, @filters.sum(input)) + assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do + @filters.sum(input, "quantity") + end + end + + def test_sum_with_unindexable_values + input = [1, true, nil, { "quantity" => 2 }] + + assert_equal(1, @filters.sum(input)) + assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do + @filters.sum(input, "quantity") + end + end + private def with_timezone(tz)