Class: Kiba::Extend::Transforms::Delete::FieldValueIfEqualsOtherField

Inherits:
Object
  • Object
show all
Defined in:
lib/kiba/extend/transforms/delete/field_value_if_equals_other_field.rb

Overview

Deletes value in delete field if that value matches value in if_equal_to field. Opinionated treatment of multivalued fields described below. Case sensitive or insensitive matching options. Can also delete associated field values (by position) in additional grouped fields. This is useful, for example, in maintaining the integrity of grouped/subgrouped multivalue fields in CollectionSpace.

Note that the value of the if_equal_to field is never modified by this transform.

Examples:

Simple example

# Used in pipeline as:
# transform Delete::FieldValueIfEqualsOtherField,
#    delete: :del, if_equal_to: :compare
xform = Delete::FieldValueIfEqualsOtherField.new(
  delete: :del,
  if_equal_to: :compare
)
input = [
  {del: "a", compare: "b"},
  {del: "c", compare: "c"}
]
result = Kiba::StreamingRunner.transform_stream(input, xform)
  .map{ |row| row }
expected = [
  {del: "a", compare: "b"},
  {del: nil, compare: "c"}
]
expect(result).to eq(expected)
# Notes
#
# The first row is left alone because a != b.
#
# In the second row, c is deleted from `del` because the value of
#   `compare` is also c.

Multival comparisons with positional_compare

# Used in pipeline as:
# transform Delete::FieldValueIfEqualsOtherField,
#    delete: :del, if_equal_to: :compare, multival: true
xform = Delete::FieldValueIfEqualsOtherField.new(
  delete: :del, if_equal_to: :compare, multival: true
)
input = [
  {del: "a|b", compare: "b"},
  {del: "b|a", compare: "b"},
  {del: "c", compare: "c|d"},
  {del: "c", compare: "d|c"},
  {del: "z|c", compare: "d|c"},
  {del: "c|c", compare: "d|c"},
  {del: "c|z", compare: "d|c"}
]
result = Kiba::StreamingRunner.transform_stream(input, xform)
  .map{ |row| row }
expected = [
  {del: "a", compare: "b"},
  {del: "a", compare: "b"},
  {del: nil, compare: "c|d"},
  {del: "c", compare: "d|c"},
  {del: "z", compare: "d|c"},
  {del: "c", compare: "d|c"},
  {del: "c|z", compare: "d|c"}
]
expect(result).to eq(expected)

Multival comparisons with non-positional_compare

# Used in pipeline as:
# transform Delete::FieldValueIfEqualsOtherField,
#    delete: :del, if_equal_to: :compare, multival: true,
#    positional_compare: false
xform = Delete::FieldValueIfEqualsOtherField.new(
  delete: :del, if_equal_to: :compare, multival: true,
  positional_compare: false
)
input = [
  {del: "a|b", compare: "b"},
  {del: "b|a", compare: "b"},
  {del: "c", compare: "c|d"},
  {del: "c", compare: "d|c"},
  {del: "z|c", compare: "d|c"},
  {del: "c|c", compare: "d|c"},
  {del: "c|z", compare: "d|c"}
]
result = Kiba::StreamingRunner.transform_stream(input, xform)
  .map{ |row| row }
expected = [
  {del: "a", compare: "b"},
  {del: "a", compare: "b"},
  {del: nil, compare: "c|d"},
  {del: nil, compare: "d|c"},
  {del: "z", compare: "d|c"},
  {del: nil, compare: "d|c"},
  {del: "z", compare: "d|c"}
]
expect(result).to eq(expected)

Even multival grouped fields, case insensitive

# Used in pipeline as:
# transform Delete::FieldValueIfEqualsOtherField,
#    delete: :del, if_equal_to: :compare, multival: true,
#    delim: ";", grouped_fields: %i[grpa grpb],
#    casesensitive: false
xform = Delete::FieldValueIfEqualsOtherField.new(
  delete: :del,
  if_equal_to: :compare,
  multival: true,
  delim: ";",
  grouped_fields: %i[grpa grpb],
  casesensitive: false
)
input = [
  {row: "1", del: "A;C;d;c;e", compare: "c", grpa: "y;x;w;u;v",
   grpb: "e;f;g;h;i"},
  {row: "2", del: "a;b;c", compare: "a;z;c", grpa: "d;e;f",
   grpb: "g;h;i"},
  {row: "3", del: "a", compare: "a;b", grpa: "d", grpb: "g"},
  {row: "4", del: "a", compare: "b", grpa: "z", grpb: "q"},
  {row: "5", del: "a", compare: "a", grpa: "z", grpb: "q"}
]
result = Kiba::StreamingRunner.transform_stream(input, xform)
  .map{ |row| row }
expected = [
  {row: "1", del: "A;d;e", compare: "c", grpa: "y;w;v",
    grpb: "e;g;i"},
  {row: "2", del: "b", compare: "a;z;c", grpa: "e", grpb: "h"},
  {row: "3", del: nil, compare: "a;b", grpa: nil, grpb: nil},
  {row: "4", del: "a", compare: "b", grpa: "z", grpb: "q"},
  {row: "5", del: nil, compare: "a", grpa: nil, grpb: nil}
]
expect(result).to eq(expected)
# ROW 1
# If `compare` is a single value, all individual values in `del`
#   are compared to the single `compare` value.
#
# In `del` field, elements 1 (C) and 3 (c) are case-insensitive
#   matches on the value in `compare`. Thus, elements 1 and 3 are
#   removed from `del` and both grouped fields.
#
# ROW 2
# If `compare` has multiple values, the values of `del` and
#   `compare` are compared positionally.
#
# Element 0 is a match (a in both). Element 1 is not (b != z).
#   Element 2 is a match (c in both).
#
# Elements 0 and 2 are removed `del` and all grouped fields.
#
# ROW 3
# `compare` is multivalued, so `del` is compared positionally
#   against `compare`, though `del` (and the grouped fields) are
#   single valued.
#
# When all values are removed from a field, `nil` is returned.
#
# ROW 4
# a != b, so row is returned unmodified.
#
# ROW 5
# a = a, so a (Element 0) is removed from `del`. Element 0 is then
#   removed from the grouped fields.

Even multival grouped, case insensitive, nonpositional

# Used in pipeline as:
# transform Delete::FieldValueIfEqualsOtherField,
#    delete: :del, if_equal_to: :compare, multival: true,
#    delim: ";", grouped_fields: %i[grpa grpb],
#    casesensitive: false, positional_compare: false
xform = Delete::FieldValueIfEqualsOtherField.new(
  delete: :del,
  if_equal_to: :compare,
  multival: true,
  delim: ";",
  grouped_fields: %i[grpa grpb],
  casesensitive: false,
  positional_compare: false
)
input = [
  {row: "1", del: "A;C;d;c;e", compare: "c", grpa: "y;x;w;u;v",
   grpb: "e;f;g;h;i"},
  {row: "2", del: "a;b;c", compare: "a;z;c", grpa: "d;e;f",
   grpb: "g;h;i"},
  {row: "2a", del: "a;b;c", compare: "z;c;a", grpa: "d;e;f",
   grpb: "g;h;i"},
  {row: "3", del: "a", compare: "a;b", grpa: "d", grpb: "g"},
  {row: "3a", del: "a", compare: "b;a", grpa: "d", grpb: "g"},
  {row: "4", del: "a", compare: "b", grpa: "z", grpb: "q"},
  {row: "5", del: "a", compare: "a", grpa: "z", grpb: "q"}
]
result = Kiba::StreamingRunner.transform_stream(input, xform)
  .map{ |row| row }
expected = [
  {row: "1", del: "A;d;e", compare: "c", grpa: "y;w;v",
    grpb: "e;g;i"},
  {row: "2", del: "b", compare: "a;z;c", grpa: "e", grpb: "h"},
  {row: "2a", del: "b", compare: "z;c;a", grpa: "e", grpb: "h"},
  {row: "3", del: nil, compare: "a;b", grpa: nil, grpb: nil},
  {row: "3a", del: nil, compare: "b;a", grpa: nil, grpb: nil},
  {row: "4", del: "a", compare: "b", grpa: "z", grpb: "q"},
  {row: "5", del: nil, compare: "a", grpa: nil, grpb: nil}
]
expect(result).to eq(expected)

Ragged multival grouped fields, case insensitive

# Used in pipeline as:
# transform Delete::FieldValueIfEqualsOtherField,
#    delete: :del, if_equal_to: :compare, multival: true,
#    delim: ";", grouped_fields: %i[grpa grpb],
#    casesensitive: false
xform = Delete::FieldValueIfEqualsOtherField.new(
  delete: :del,
  if_equal_to: :compare,
  multival: true,
  delim: ";",
  grouped_fields: %i[grpa grpb],
  casesensitive: false
)
input = [
  {del: "A;C;d;e;c", compare: "c", grpa: "y;x;w;u",
    grpb: "e;f;g;h;i"}
]
result = Kiba::StreamingRunner.transform_stream(input, xform)
  .map{ |row| row }
expected = [
  {del: "A;d;e", compare: "c", grpa: "y;w;u", grpb: "e;g;h"}
]
expect(result).to eq(expected)
# This triggers a warning printed to STDOUT, which may trigger you
#   to examine the input data:

# ~~~
# KIBA WARNING: One or more grouped fields (grpa, grpb) has
#   different number of values than the others in {:del=>"A;d;e",
#   :compare=>"c", :grpa=>"y;x;w;u", :grpb=>"e;f;g;h;i"}
# ~~~

# If `del` had 4 elements and one or more of the grouped fields had
#   a different number of elements, this would be handled similarly,
#   with a slightly different warning.

# `grpa` has 4 values, while `grpb` has 5.
#
# Elements 1 and 4 from `del` match `compare`, so they are deleted.
#   Those elements are also deleted from the grouped fields if
#   present.

Instance Method Summary collapse

Constructor Details

#initialize(delete:, if_equal_to:, multival: false, positional_compare: true, delim: nil, grouped_fields: [], casesensitive: true) ⇒ FieldValueIfEqualsOtherField

Returns a new instance of FieldValueIfEqualsOtherField.

Parameters:

  • delete (Symbol)

    field from which values will be deleted

  • if_equal_to (Symbol)

    field the delete values will be compared to. In other words, the “other field”

  • multival (Boolean) (defaults to: false)

    whether to split field values for comparison

  • positional_compare (Boolean) (defaults to: true)

    whether to compare multivalues positionally. Only relevant if multival is true and if_equal_to is multivalued

  • delim (String) (defaults to: nil)

    on which to split if multival. Defaults to Kiba::Extend.delim if not provided.

  • grouped_fields (Array<Symbol>) (defaults to: [])

    field(s) from which positionally corresponding values should also be removed

  • casesensitive (Boolean) (defaults to: true)

    matching mode



267
268
269
270
271
272
273
274
275
276
277
# File 'lib/kiba/extend/transforms/delete/field_value_if_equals_other_field.rb', line 267

def initialize(delete:, if_equal_to:,
  multival: false, positional_compare: true, delim: nil,
  grouped_fields: [], casesensitive: true)
  @delete = delete
  @compare = if_equal_to
  @multival = multival
  @positional = positional_compare
  @delim = delim || Kiba::Extend.delim
  @group = grouped_fields
  @casesensitive = casesensitive
end

Instance Method Details

#process(row) ⇒ Object

Parameters:

  • row (Hash{ Symbol => String, nil })


280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/kiba/extend/transforms/delete/field_value_if_equals_other_field.rb', line 280

def process(row)
  del_val = prepare_val(delete, row, :compare)
  compare_val = prepare_val(compare, row, :compare)
  return row if compare_val.nil? || del_val.blank?

  compare_method = get_compare_method(del_val, compare_val)
  to_delete = method(compare_method).call(del_val, compare_val)
  return row if to_delete.empty?

  orig_del_val = prepare_val(delete, row, :final)
  row[delete] = do_deletes(to_delete, orig_del_val.dup)
  return row unless grouped?

  grouped = group.map { |field| prepare_val(field, row) }
  validation = validate_groups(grouped, orig_del_val)
  report_group_issue(validation, row) unless validation == :valid
  grouped.map { |grp| do_deletes(to_delete, grp) }
    .each_with_index { |grp, i| row[group[i]] = grp }

  row
end