|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +module Kiba |
| 4 | + module Extend |
| 5 | + module Transforms |
| 6 | + module Delete |
| 7 | + # Delete a field value if the arbitrary Lambda passed in evaluates to |
| 8 | + # true |
| 9 | + # |
| 10 | + # @example Without delim |
| 11 | + # xform = Delete::FieldValueConditional.new( |
| 12 | + # fields: %i[a b], |
| 13 | + # lambda: ->(val, row) { row[:c] == val } |
| 14 | + # ) |
| 15 | + # |
| 16 | + # input = [ |
| 17 | + # {a: nil, b: "", c: "c"}, |
| 18 | + # {a: "c", b: "b", c: "c"}, |
| 19 | + # {a: "a|c", b: "c", c: "c"} |
| 20 | + # ] |
| 21 | + # result = Kiba::StreamingRunner.transform_stream(input, xform) |
| 22 | + # .map{ |row| row } |
| 23 | + # expected = [ |
| 24 | + # {a: nil, b: nil, c: "c"}, |
| 25 | + # {a: nil, b: "b", c: "c"}, |
| 26 | + # {a: "a|c", b: nil, c: "c"} |
| 27 | + # ] |
| 28 | + # expect(result).to eq(expected) |
| 29 | + # |
| 30 | + # @example With delim |
| 31 | + # xform = Delete::FieldValueConditional.new( |
| 32 | + # fields: %i[a b], |
| 33 | + # lambda: ->(val, row) { row[:c] == val }, |
| 34 | + # delim: "|" |
| 35 | + # ) |
| 36 | + # |
| 37 | + # input = [ |
| 38 | + # {a: nil, b: "", c: "c"}, |
| 39 | + # {a: "c", b: "b", c: "c"}, |
| 40 | + # {a: "a|c", b: "c", c: "c"} |
| 41 | + # ] |
| 42 | + # result = Kiba::StreamingRunner.transform_stream(input, xform) |
| 43 | + # .map{ |row| row } |
| 44 | + # expected = [ |
| 45 | + # {a: nil, b: nil, c: "c"}, |
| 46 | + # {a: nil, b: "b", c: "c"}, |
| 47 | + # {a: "a", b: nil, c: "c"} |
| 48 | + # ] |
| 49 | + # expect(result).to eq(expected) |
| 50 | + # |
| 51 | + class FieldValueConditional |
| 52 | + include BooleanLambdaParamable |
| 53 | + |
| 54 | + # @param fields [Array<Symbol>,Symbol] field(s) to delete from |
| 55 | + # @param lambda [Lambda] with one parameter for row to be passed in |
| 56 | + # through. The Lambda must take `val` and `row` positional arguments |
| 57 | + # and evaulate to/return `TrueClass` or `FalseClass` |
| 58 | + # @param delim [nil, String] if provided, turns on multivalue mode; |
| 59 | + # each whole field value is split into values, each of which is |
| 60 | + # sent as `val` to the lambda |
| 61 | + def initialize(fields:, lambda:, delim: nil) |
| 62 | + @fields = [fields].flatten |
| 63 | + @lambda = lambda |
| 64 | + @multival = delim ? true : false |
| 65 | + @delim = delim || Kiba::Extend.delim |
| 66 | + end |
| 67 | + |
| 68 | + # @param row [Hash{ Symbol => String, nil }] |
| 69 | + # @raise [Kiba::Extend::BooleanReturningLambdaError] if given lambda |
| 70 | + # does not evaluate to `TrueClass` or `FalseClass` using |
| 71 | + # the first row of data passed to the `process` method |
| 72 | + def process(row) |
| 73 | + test_lambda(["foo", row]) unless lambda_tested |
| 74 | + |
| 75 | + fields.each { |field| row[field] = delete_from_field(row, field) } |
| 76 | + row |
| 77 | + end |
| 78 | + |
| 79 | + private |
| 80 | + |
| 81 | + attr_reader :fields, :lambda, :multival, :delim |
| 82 | + |
| 83 | + def delete_from_field(row, field) |
| 84 | + val = row[field] |
| 85 | + return if val.blank? |
| 86 | + return do_deletes([val], row, field) unless multival |
| 87 | + |
| 88 | + do_deletes(val.split(delim), row, field) |
| 89 | + end |
| 90 | + |
| 91 | + # @param vals [Array<String>] |
| 92 | + def do_deletes(vals, row, field) |
| 93 | + result = vals.map { |val| lambda.call(val, row) ? nil : val } |
| 94 | + .compact |
| 95 | + return nil if result.empty? |
| 96 | + |
| 97 | + result.join(delim) |
| 98 | + end |
| 99 | + end |
| 100 | + end |
| 101 | + end |
| 102 | + end |
| 103 | +end |
0 commit comments