Skip to content

Instantly share code, notes, and snippets.

@StevenJL
Created September 11, 2024 06:56
Show Gist options
  • Save StevenJL/32469f4aed8b5e5b5cd4edea82999fca to your computer and use it in GitHub Desktop.
Save StevenJL/32469f4aed8b5e5b5cd4edea82999fca to your computer and use it in GitHub Desktop.
find_moved_element
# Algorithm to detect delete/insertion change (ie. drag-n-drop) in array of unique elements
# From https://webshittery.com/blog/cumbersome-apis/
# Note that in certain situations where there is more than one correct answer
# For example:
#
# find_moved_element([1, 2, 3, 4, 5], [1, 3, 2, 4, 5]),
#
# Correct answers include:
#
# { moved_element: 3, original_index: 2, new_index: 1 }
# { moved_element: 2, original_index: 1, new_index: 2 }
#
# Another example:
# find_moved_element([1, 2, 3, 4, 5], [2, 3, 4, 5, 1]),
#
# Only one correct answer:
# { moved_element: 1, original_index: 0, new_index: 4 }
# N.B. "moved" is defined as deleting element, then inserting element back (think of drag-n-drop).
def find_moved_element(array_orig, array_moved)
if array_orig == array_moved
raise 'You didnt move anything, doofus!'
end
orig_index_hash = {}
array_orig.each do |el|
orig_index_hash[el] = array_orig.index(el)
end
moved_index_hash = {}
array_moved.each do |el|
moved_index_hash[el] = array_moved.index(el)
end
changed_value = nil
# Let's find the changed value!
array_orig.each do |el|
# Same index, means its unmoved
next if moved_index_hash[el] == orig_index_hash[el]
# Most of the time an absolute value difference of exactly 1
# means this also wasn't the changed value. There are edge-cases so lets
# handle that below.
next if (moved_index_hash[el] - orig_index_hash[el]).abs == 1
changed_value = el
break
end
# The edgecase occurred where the deleted element
# was then inserted exactly one away from its original position.
# In which case, any element that has a different index than
# before was the one that was moved.
unless changed_value
array_orig.each do |el|
# Same index, means its unmoved
next if moved_index_hash[el] == orig_index_hash[el]
changed_value = el
break
end
end
original_index = array_orig.index(changed_value)
new_index = array_moved.index(changed_value)
{
moved_element: changed_value,
original_index: original_index,
new_index: new_index,
}
end
# TEST CASES
a = [1, 2, 3, 4, 5, 6, 7]
b = [1, 3, 2, 4, 5, 6, 7]
answer = find_moved_element(a, b)
unless answer == { moved_element: 2, original_index: 1, new_index: 2 }
raise "You dun goofed, doofus for #{b}"
end
a = [1, 2, 3, 4, 5, 6, 7]
b = [2, 3, 4, 5, 1, 6, 7]
answer = find_moved_element(a, b)
unless answer == { moved_element: 1, original_index: 0, new_index: 4 }
raise "You dun goofed, doofus for #{b}"
end
a = [1, 2, 3, 4, 5, 6, 7]
b = [7, 1, 2, 3, 4, 5, 6]
answer = find_moved_element(a, b)
unless answer == { moved_element: 7, original_index: 6, new_index: 0 }
raise "You dun goofed, doofus for #{b}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment