Skip to content

Commit

Permalink
Retain original ordering of keys when diffing hashes
Browse files Browse the repository at this point in the history
If you have two hashes and there are missing or extra keys on either
side, remember what the order of those keys were and use that same
ordering when producing the diff instead of sticking those keys at the
end of the diff.
  • Loading branch information
mcmire committed May 16, 2020
1 parent 91a6e07 commit 3387189
Show file tree
Hide file tree
Showing 8 changed files with 436 additions and 129 deletions.
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Metrics/BlockLength:
- shared_context
- shared_examples
- shared_examples_for
Metrics/BlockNesting:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Expand Down Expand Up @@ -115,10 +117,14 @@ Style/NegatedIf:
Enabled: false
Style/NegatedWhile:
Enabled: false
Style/Next:
Enabled: false
Style/NumericPredicate:
Enabled: false
Style/OneLineConditional:
Enabled: false
Style/ParallelAssignment:
Enabled: false
Style/PercentLiteralDelimiters:
Enabled: false
Style/PreferredHashMethods:
Expand Down
69 changes: 30 additions & 39 deletions lib/super_diff/operational_sequencers/array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,15 @@ class LcsCallbacks
public :operations

def match(event)
operations << ::SuperDiff::Operations::UnaryOperation.new(
name: :noop,
collection: actual,
key: event.new_position,
value: event.new_element,
index: event.new_position,
index_in_collection: actual.index(event.new_element),
)
add_noop_operation(event)
end

def discard_a(event)
operations << ::SuperDiff::Operations::UnaryOperation.new(
name: :delete,
collection: expected,
key: event.old_position,
value: event.old_element,
index: event.old_position,
index_in_collection: expected.index(event.old_element),
)
add_delete_operation(event)
end

def discard_b(event)
operations << ::SuperDiff::Operations::UnaryOperation.new(
name: :insert,
collection: actual,
key: event.new_position,
value: event.new_element,
index: event.new_position,
index_in_collection: actual.index(event.new_element),
)
add_insert_operation(event)
end

def change(event)
Expand All @@ -77,21 +56,6 @@ def change(event)

private

def add_change_operation(event, child_operations)
operations << ::SuperDiff::Operations::BinaryOperation.new(
name: :change,
left_collection: expected,
right_collection: actual,
left_key: event.old_position,
right_key: event.new_position,
left_value: event.old_element,
right_value: event.new_element,
left_index: event.old_position,
right_index: event.new_position,
child_operations: child_operations,
)
end

def add_delete_operation(event)
operations << Operations::UnaryOperation.new(
name: :delete,
Expand All @@ -113,6 +77,33 @@ def add_insert_operation(event)
index_in_collection: actual.index(event.new_element),
)
end

def add_noop_operation(event)
operations << ::SuperDiff::Operations::UnaryOperation.new(
name: :noop,
collection: actual,
key: event.new_position,
value: event.new_element,
index: event.new_position,
index_in_collection: actual.index(event.new_element),
)
end

def add_change_operation(event, child_operations)
operations << ::SuperDiff::Operations::BinaryOperation.new(
name: :change,
left_collection: expected,
right_collection: actual,
left_key: event.old_position,
right_key: event.new_position,
left_value: event.old_element,
right_value: event.new_element,
left_index: event.old_position,
right_index: event.new_position,
child_operations: child_operations,
)
end

end
end
end
Expand Down
77 changes: 47 additions & 30 deletions lib/super_diff/operational_sequencers/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,7 @@ def self.applies_to?(_expected, _actual)
method_object [:expected!, :actual!]

def call
i = 0
operations = build_operation_sequence

while i < unary_operations.length
operation = unary_operations[i]
next_operation = unary_operations[i + 1]
child_operations = possible_comparison_of(operation, next_operation)

if child_operations
operations << Operations::BinaryOperation.new(
name: :change,
left_collection: operation.collection,
right_collection: next_operation.collection,
left_key: operation.key,
right_key: operation.key,
left_value: operation.collection[operation.key],
right_value: next_operation.collection[operation.key],
left_index: operation.index,
right_index: operation.index,
child_operations: child_operations,
)
i += 2
else
operations << operation
i += 1
end
end

operations
compressed_operations
end

protected
Expand All @@ -53,6 +25,51 @@ def build_operation_sequence

private

def compressed_operations
unary_operations = self.unary_operations
compressed_operations = build_operation_sequence
unmatched_delete_operations = []

unary_operations.each_with_index do |operation, index|
if (
operation.name == :insert &&
(delete_operation = unmatched_delete_operations.find { |op| op.key == operation.key }) &&
(insert_operation = operation)
)
unmatched_delete_operations.delete(delete_operation)

if (child_operations = possible_comparison_of(
delete_operation,
insert_operation,
))
compressed_operations.delete(delete_operation)
compressed_operations << Operations::BinaryOperation.new(
name: :change,
left_collection: delete_operation.collection,
right_collection: insert_operation.collection,
left_key: delete_operation.key,
right_key: insert_operation.key,
left_value: delete_operation.collection[operation.key],
right_value: insert_operation.collection[operation.key],
left_index: delete_operation.index_in_collection,
right_index: insert_operation.index_in_collection,
child_operations: child_operations,
)
else
compressed_operations << insert_operation
end
else
if operation.name == :delete
unmatched_delete_operations << operation
end

compressed_operations << operation
end
end

compressed_operations
end

def possible_comparison_of(operation, next_operation)
if should_compare?(operation, next_operation)
sequence(operation.value, next_operation.value)
Expand All @@ -65,7 +82,7 @@ def should_compare?(operation, next_operation)
next_operation &&
operation.name == :delete &&
next_operation.name == :insert &&
next_operation.index == operation.index
next_operation.key == operation.key
end

def sequence(expected, actual)
Expand Down
Loading

0 comments on commit 3387189

Please sign in to comment.