How to 'set length' or resize dynamic array in Oxygene?

I’m a bit at a loss on how to resize a dynamic array in Oxygene. ‘SetLength’ method does not seem to be recognized. ChatBot/ChatGPT/Claude2 all seem to gravitate towards ‘Add()’ and ‘Insert()’ methods that are for the .Net List<> type. :disguised_face:

Here’s a simple example in Object Pascal that I’d like to translate to Oxygene:

program DynamicStringArray;

{$mode delphi}{$H+}

type 
  StrArr = array of string; 
var
  myArray: StrArr;

procedure AppendToStringArray(var arr: StrArr; const value: string); 
begin
  SetLength(arr, Length(arr) + 1);
  arr[High(arr)] := value;
end;

var
  i: Integer;
  elem: String;

begin
  // Initialize the dynamic string array
  SetLength(myArray, 0);

  // Append strings to the dynamic array
  AppendToStringArray(myArray, 'Hello');
  AppendToStringArray(myArray, 'World');

  // Display the contents of the array
  for i := Low(myArray) to High(myArray) do
    Write(myArray[i], ' ');

  writeln;

  myArray := myArray + ['!!!']; // Delphi XE 7+ syntax - requires compiler directive {$mode delphi}

  for elem in myArray do  // using 'foreach' loop
    Write(elem, ' ');
end.


This is what I’ve come up with so far using the Array.Resize() method. It works, but I’m not sure how efficient this is since it creates a new array each time the AppendToStringArray method is called.

namespace ArrDyna_Resize_Oxy;

type
  StrArr = array of String;
  
var
  myArray: StrArr;

procedure AppendToStringArray(var arr: StrArr; const value: String);
begin
  Array.Resize(var arr, length(arr) + 1);
  arr[high(arr)] := value;
end;

begin
  // no need to initialize it appears
  // myArray := new String[0];

  // Append strings to the dynamic array
  AppendToStringArray(var myArray, 'Hello');
  AppendToStringArray(var myArray, 'World');
  AppendToStringArray(var myArray, '!!!');
  
  // Display the contents of the array
  for elem in myArray do 
  begin
    write(elem); write(' ');
  end;
  writeLn;
end.

Hello Gary,
I suppose it’s .NET.
Why don’t you use List <String>?

1 Like

Hey Patrick,
Yes, the List<> type would seem to be the obvious choice. But, I’m a bit more familiar with the Object Pascal world. I was reading an older article by Marco Cantu about dynamic arrays in Delphi.

var
  di: array of Integer;
  i: Integer;
begin
  di := [1, 2, 3];    // initialization
  di := di + di;      // concatenation
  di := di + [4, 5];  // mixed concatenation

  for i in di do
  begin
    Memo1.Lines.Add (i.ToString);
  end;

And I thought would try it out in Oxygene. I realize Oxygene rides between the worlds of Object Pascal and .Net CLR. But it’s not always clear which methods are from Object Pascal or from .Net. and So it appears certain mainstay functions used for dynamic arrays in Object Pascal like ‘setLength’ do not appear to work. The Oxygene documentation does not really go into any methods that can be used with Dynamic arrays. It essentially goes over declaring, initializing, and setting an index one at a time. There is no mention of resizing arrays, etc… So I had to look up methodologies and methods employed by both Object Pascal/Delphi and .Net CLR languages and see which ones Oxygene uses.

I’ve been spending the last couple days doing research on potential solutions or equivalents without resorting to List type … at least not right away. I’m just curious like that. I also work out some of the code in another Pascal.Net variant.(a spiritual brother of Oxygene) to see how their methods may be tied to .Net or Object Pascal or both.


This PascalABC.Net code demonstrates simply concatenating one dynamic array to another using the + operator with no conversions to another type - similar to Delphi in which the integer array type is perserved.

If I were to do the same in Oxygene, the sumArr would get inferred and converted to System.Linq.Enumerable+Concat2Iterator[System.Int32] which is a generic type that represents an iterator over the concatenation of two sequences of integers].

According to ChatGPT4:

You are right, using + can also work for concatenating arrays in C#, but it is not a direct operator for arrays. It is actually a syntactic sugar for the Queryable.Concat method1, which is a LINQ extension method that works on any IQueryable<T> source2. However, it can only be used when bringing together two arrays. Assuming the destination array has enough space, Array.Copy will work faster than + or Concat3

In Oxygene, if I wish to maintain the System.Int32[] type in Oxygene, then I must cast to array. I used the Concat() method as it appears to be faster than the syntactic sugar behind the overloaded + operator as mentioned by ChatGPT4.


So I believe that this is essentially one solution.

There’s much one could do with their own home-grown methods as demonstrated below. But it’s probably safer to use List<> type in Oxygene. But this was a fun academic detour for me.

namespace AddDynamicArrays_Oxy;

uses
  System;

type
  DynamicArray = public class
  private
    myArr: array of Integer; private;
    count: Integer; private;
    size: Integer; private;

 public 
    constructor; 
    begin
      myArr := new Integer[1];
      count := 0;
      size := 1;
    end;

    method add(data: Integer);
    begin
      if count = size then
        growSize();
      myArr[count] := data;
      inc(count);
    end;

    method growSize;
    begin
      var temp: array of Integer := nil;
      if count = size then
      begin
        temp := new Integer[size * 2];
        for i: Integer := 0 to size - 1 do
          temp[i] := myArr[i];
      end;
      myArr := temp;
      size := size * 2;
    end;

    method shrinkSize;
    begin
      var temp: array of Integer := nil;
      if count > 0 then begin
        temp := new Integer[count];
        for i: Integer := 0 to count - 1 do
          temp[i] := myArr[i];
        size := count;
        myArr := temp;
      end;
    end;

    method addAt(index: Integer; data: Integer);
    begin
      if count = size then
        growSize();
      for i: Integer := count - 1 downto index do
        myArr[i + 1] := myArr[i];
      myArr[index] := data;
      inc(count);
    end;

    method remove;
    begin
      if count > 0 then begin
        myArr[count - 1] := 0;
        dec(count);
      end;
    end;

    method removeAt(index: Integer);
    begin
      if count > 0 then begin
        for i: Integer := index to count - 1 - 1 do
          myArr[i] := myArr[i + 1];
        myArr[count - 1] := 0;
        dec(count);
      end;
    end;


    class method Main;
    begin
      var dynArr: DynamicArray := new DynamicArray();
      dynArr.add(1);
      dynArr.add(2);
      dynArr.add(3);
      dynArr.add(4);
      dynArr.add(5);
      dynArr.add(6);
      dynArr.add(7);
      dynArr.add(8);
      dynArr.add(9);
      //  print all array elements after adding 9 elements
      writeLn("Elements of array:");
      for i: Integer := 0 to dynArr.size - 1 do
        write(dynArr.myArr[i] + " ");
      writeLn();
      //  print size of array and num of elements
      writeLn("Size of array: " + dynArr.size);
      writeLn("No of elements in array: " + dynArr.count);
      //  shrinkSize of array
      dynArr.shrinkSize();
      //  print all array elements
      writeLn("Elements of myArr " + "after shrinkSize of myArr:");
      for i: Integer := 0 to dynArr.size - 1 do
        write(dynArr.myArr[i] + " ");
      writeLn();
      //  print size of myArr and num of elements
      writeLn("Size of myArr: " + dynArr.size);
      writeLn("No of elements in myArr: " + dynArr.count);
      //  add an element at index 1
      dynArr.addAt(1, 22);
      //  print Elements of myArr after adding an element at index 1
      writeLn("Elements of myArr after" + " add an element at index 1:");
      for i: Integer := 0 to dynArr.size - 1 do
        write(dynArr.myArr[i] + " ");
      writeLn();
      //  print size of myArr and num of elements
      writeLn("Size of myArr: " + dynArr.size);
      writeLn("No of elements in myArr: " + dynArr.count);
      //  delete last element
      dynArr.remove();
      //  print Elements of myArr after delete last element
      writeLn("Elements of myArr after" + " delete last element:");
      for i: Integer := 0 to dynArr.size - 1 do
        write(dynArr.myArr[i] + " ");
      writeLn();
      //  print size of myArr and num of elements
      writeLn("Size of myArr: " + dynArr.size);
      writeLn("No of elements in myArr: " + dynArr.count);
      //  delete element at index 1
      dynArr.removeAt(1);
      //  print Elements of myArr after delete an element index 1
      writeLn("Elements of myArr after" + " delete element at index 1:");
      for i: Integer := 0 to dynArr.size - 1 do
        write(dynArr.myArr[i] + " ");
      writeLn();
      //  print size of myArr and num of element
      writeLn("Size of myArr: " + dynArr.size);
      writeLn("No of elements in myArr: " + dynArr.count);
    end;
  end;
end.

Last but not least, the article by Marco Cantú, that got me started on all this. Here’s one way to transcribe Marco’s Delphi code to Oxygene: :cowboy_hat_face:

namespace ArrayConcatStrings;

var
  di: array of Integer;
  
begin
  di := [1, 2, 3];
  di := (di + di).ToArray;
  di := (di + [4, 5]).ToArray;
  
  writeLn(di.GetType); // System.Int32[]
  
  for each i in di do
  begin
    write(i); write(' '); // 1 2 3 1 2 3 4 5
  end;
  writeLn;
end.

To be correct, Oxygene was first an implementation of Pascal for .NET, then RemObjects added support for more targets: Java, Win32, MacOS, … They have written a library that defines plenty of tools, in a way that is target-independant.

I’ve written more than 1M lines of code in Oxygene, for .NET only. So I never use their library, as the .NET libraries are enough, sometimes adding nuGet packages for additional tools.

That’s for this that the List <T> class was made.

Be careful: I had cases with ChatGPT returns wrong information.

It will be better to use Array.CopyTo to resize your array, especially in .NET, where the compiler uses AVX (vectorized) instructions:

method growSize;
require
  count = size;
begin
  var temp := new Integer[size * 2];
  myArr.CopyTo (temp, 0);
  myArr := temp;
  size := size * 2;
end;

Your code was wrong, count must always be equal to size when entering the methoid. In addition, if count is different than size, your method kills the content of the array.
Note also that, even with my correction, the method is not thread-safe.

1 Like

Thank you Patrick for your adept insight! 1M lines of code in Oxygene! That’s amazing! I’m about 990,000 lines of code behind you and none of it is for actual production … just for learning :laughing: Sounds like I need to focus on the .Net ecosystem. :wink: Yes, I concur - AI can give hilariously wrong information and the more obscure the language the more hilarious it can get. Thank you for doing the code review for me and pointing out the use of Array.ToCopyTo … I have much to learn :nerd_face:

1 Like