Atak_Snajpera
Atak_Snajpera

Reputation: 629

How to shift data in array?

 ///Example
 some_array[0]:=0;
 some_array[1]:=1;
 some_array[2]:=2;
 some_array[3]:=3;
 some_array[4]:=4;

And now I need to shift values in array like this (by one cell up)

 some_array[0]:=1;
 some_array[1]:=2;
 some_array[2]:=3;
 some_array[3]:=4;
 some_array[4]:=0;

Is there any build in procedure or I have to do this manually by copying to some temporary array?

Upvotes: 2

Views: 3208

Answers (4)

Andy k
Andy k

Reputation: 1116

This array rotation function is fast if your not too worried about transient memory overhead.

Just change the array type to anything of your choice.

Positive values of x rotate right, negative rotate left.

function RotateArray(list:Tintarray; x:integer):Tintarray;
var n:integer;
begin
  n:=length(list);
  x:=x mod n;
  if (n<2) or (x=0) then
    result:=Copy(list)
  else
  if x>0 then
    result:=Copy(list,n-x,x) + Copy(list,0,n-x)
  else
    result:=Copy(list,-x,n-x) + Copy(list,0,-x)
end;

Upvotes: 0

MyICQ
MyICQ

Reputation: 1158

Stumbling across this one while facing a similar issue.

I have not implemented it yet, but have thought about this different approach: KEEP the array as it is, but make a new procedure to read values where you change the "zero" position.

Example:

read_array(index: integer; shift: integer)..

So if your original array is read with this function, using shift "1" it would read "1,2,3,4,0" (obviously looping). It would require you to keep track of a few things, but would not require modifying anything. So performance should be greater for very large arrays.

Similar would work for other types as well.

EDIT: an example function with free start index and variable step size plus sample size is here:

function get_shifted_array(inArray: TStringList; startindex,
  lineCount: integer;
  stepsize: integer): TStringList;

var
   i : integer;           // temp counter
   nextindex : integer;   // calculate where to get next value from...
   arraypos  : integer;   // position in inarray to take
   temp      : tstringlist;


        // function to mimic excel Remainder( A,B) function
        // in this   remainder(-2,10) = 8
        //
           function modPositive( dividend, divisor: integer): integer;
           var
              temp : integer;

            begin

               if dividend < 0 then
                   begin
                       temp := abs(dividend) mod divisor;   // 1 mod 10 =9    for -1
                                                            // 122 mod 10 = 2  for -122
                       result :=  (divisor - temp);
                   end
               else
                  result := dividend mod divisor;
            end;


begin

   nextindex := startindex;             // where in input array to get info from
   temp := tstringlist.create;          // output placeholder

   for i := 1 to lineCount do
      begin

          // convert to actual index inside loop
          arraypos := modPositive(nextindex, inarray.count);     // handle it like Excel: remainder(-1,10) = 9

          // if mod is zero, we get array.count back. Need zero index then.
          if arraypos = inArray.Count then arraypos := 0;        // for negative loops.

          // get the value at array position
          temp.Add( 'IDX=' + inttostr(arraypos) + ' V=' +   inarray[  arraypos ] );

          // where to go next
          // should we loop ?
          if ((nextindex+ stepsize +1)> inArray.Count ) then
                 begin
                     nextindex :=  (nextindex + stepsize ) mod inArray.Count;
                 end
          else
              nextindex := nextindex + stepsize;
      end;

   result := temp;

end;

Thereby:

get_shifted_array(
    inputarray, 
    -1,    // shiftindex
    length(inputarray), 
   1 )  // stepsize

would return the array shifted backwards one place.

All without any modification to array.

Upvotes: 3

LU RD
LU RD

Reputation: 34929

There is no such procedure in the RTL.

A generic procedure (as proposed by @DavidHeffernan) might look something like this:

Type
  TMyArray = record
    class procedure RotateLeft<T>(var a: TArray<T>); static;
  end;

class procedure TMyArray.RotateLeft<T>(var a: TArray<T>);
var
  tmp : T;
  i : Integer;
begin
  if Length(a) > 1 then begin
    tmp := a[0];
    for i := 1 to High(a) do
      a[i-1] := a[i];
    a[High(a)] := tmp;
  end;
end;

var
  a: TArray<Integer>;
  i:Integer;    
begin
  SetLength(a,5);
  for i := 0 to High(a) do a[i] := i;
  TMyArray.RotateLeft<Integer>(a);
  for i := 0 to High(a) do WriteLn(a[i]);

  ReadLn;
end.

A low level routine using Move() could be used if performance is critical:

class procedure TMyArray.RotateLeft<T>(var a: TArray<T>);
var
  tmp : T;
begin
  if Length(a) > 1 then begin
    Move(a[0],tmp,SizeOf(T));          // Temporary store the first element
    Move(a[1],a[0],High(a)*SizeOf(T));
    Move(tmp,a[High(a)],SizeOf(T));    // Put first element last
    // Clear tmp to avoid ref count drop when tmp goes out of scope
    FillChar(tmp,SizeOf(T),#0);
  end;
end;

Note the FillChar() call to clear the temporary variable at the end. If T is a managed type, it would otherwise drop the reference count of the last array element when going out of scope.

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 613461

There is no built in function for this. You will need to write your own. It might look like this:

procedure ShiftArrayLeft(var arr: array of Integer);
var
  i: Integer;      
  tmp: Integer;
begin
  if Length(arr) < 2 then
    exit;

  tmp := arr[0];
  for i := 0 to high(arr) - 1 do
    arr[i] := arr[i + 1];
  arr[high(arr)] := tmp;
end;

Note that there is no need to copy to a temporary array. You only need to make a temporary copy of one element.

If your arrays are huge then the copying overhead could be significant. In which case you would be better off using a circular array. With a circular array you remember the index of the first element. Then the shift operation is just a simple increment or decrement operation on that index, modulo the length of the array.

If you use a modern Delphi then this could readily be converted to a generic method. And I think it should be easy enough for you to write the shift in the opposite direction.

Upvotes: 4

Related Questions