4

I'm trying to create a function that 'drags' a sequential number of elements to a new location within the array, constrained to the current size of the array. Other items should jiggle round the 'dragged' items.

For example, if my array has 7 elements and I want to drag the middle three...

1, 2, 3, 4, 5, 6, 7  <-- keys
a, b, C, D, E, f, g  <-- values

The uppercase chars are the ones I want to 'drag'. If I drag to start of the array (drag to 1) the array would look like this:

1, 2, 3, 4, 5, 6, 7  <-- keys
C, D, E, a, b, f, g  <-- values

If I drag to position 5 (or above - can't be dragged outside current array size) the array would look like this:

1, 2, 3, 4, 5, 6, 7  <-- keys
a, b, f, g, C, D, E  <-- values

Any idea how I can achieve that using Lua in a non-crufty manner?

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
cabbageforall
  • 620
  • 1
  • 5
  • 12
  • How big are the tables involved? How large are the dragged chains? I can think of two main ways to do this offhand but they scale differently to larger sized tables. – Etan Reisner Aug 21 '15 at 02:25
  • small tables and chains for the stuff i want to do, but if others use the code then potentially big tables with big chains. – cabbageforall Aug 21 '15 at 02:45
  • Don't have time to write out an answer at the moment but one of the ideas is basically just get your start index and count `table.unpack(t, 1, start)` + `table.unpack(t, start + count)` + `table.unpack(t, start, start+count)` (with whatever `+`/`-` 1 is necessary on those to make it work. Though that requires looping to work. The other is repeated `table.remove`/`table.insert` pairs. – Etan Reisner Aug 21 '15 at 03:16

2 Answers2

2

Here is a version using table.move available in Lua 5.3.

It copies the group to be dragged into another table and shifts values up or down to make room for the group.

 function drag(t, src, len, dest)
    local copy = table.move(t, src, src + len - 1, 1, {})

    if src >= dest then
        table.move(t, dest, src - 1, dest + len)
    else 
        table.move(t, src + len, dest + len - 1, src)
    end

    table.move(copy, 1, len, dest, t)
 end
ryanpattison
  • 6,151
  • 1
  • 21
  • 28
  • That works very nicely! Despite the somewhat sparse official documentation, I'm starting to realise `table.move()` is a very flexible and useful function, especially considering its internal optimisations. – cabbageforall Aug 26 '15 at 21:03
1
function drag(t, src, len, dest)
  if len == 0 then return end
  local left, ctr, start, index, elem = math.min(src, dest), 0, 0
  local rot, size = dest - src, src + dest + len - 2 * left
  repeat
    start, index, elem = start + 1, start, t[left + start]
    repeat
      index = (index + rot) % size
      ctr, t[left + index], elem = ctr + 1, elem, t[left + index]
    until index < start
  until ctr == size
end

for K = 1, 5 do
  local tbl = {'a', 'b', 'C', 'D', 'E', 'f', 'g'}
  drag(tbl, 3, 3, K)
  print(table.concat(tbl))
end
Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
  • Almost ;) It breaks if src + len > dest, eg: __for K = 1, 7 do local tbl = {'a', 'b', 'C', 'D', 'E', 'f', 'g'} drag(tbl, 3, 3, K) print(table.concat(tbl)) end__ – cabbageforall Aug 21 '15 at 14:27
  • @Aubergine18 - You are trying to drag selected elements beyond the array ;) An assertion `assert(math.max(src, dest) + len - 1 <= #t)` should be added if you wish to check this condition. – Egor Skriptunoff Aug 21 '15 at 14:46