Runtime Arrays using Create - arrays

Say i have
ArrayOfTXSDecimal = array of TXSDecimal;
Then during runtime i do
Ids := ArrayOfTXSDecimal.create(14450);
What did i just create? an array(ids) with 14450 indexs or just index 14450

You are creating a dynamic array with one element whose value is 14450. You are doing the equivalent of this:
SetLength(Ids, 1);
Ids[0] := 14450;
This Create() syntax for dynamic arrays is documented on Embarcadero's DocWiki:
An alternative method of allocating memory for dynamic arrays is to invoke the array constructor:
type
TMyFlexibleArray = array of Integer;
begin
MyFlexibleArray := TMyFlexibleArray.Create(1, 2, 3 {...});
end;
which allocates memory for three elements and assigns each element the given value.

Related

Delphi: How to reference an array from within a class

I work with arrays and have tested the functionality within the context of a class, like:
Ttest = class
values : array of integer;
procedure doStuff;
end;
The methods, like doStuff all operate on the values array without the need to pass the array as a parameter. This suits me and it is fast.
Now I want to use this class to work with an external array, like Ttest.create(myValues) In the constructor I could copy myValues to the internal values but that would be quite a waste and moreover, at the end the copy would have to be reversed to pass the updated values back.
My question is how can I extend this class so that it can efficiently work with an external array. In pseudo code like this:
constructor create(var myValues : array of integer);
begin
address of values := address of myValues;
doSTuff;
end;
Lesson 1
In Delphi, dynamic arrays are reference types. A variable of dynamic array type contains only a pointer to the actual dynamic array heap object, and in an assignment,
A := B
where A and B are dynamic arrays of the same type, the dynamic array heap object isn't copied. The only thing that happens is that A and B will point to the same dynamic array heap object (that is, the 32- or 64-bit B pointer is copied to A) and that the reference count of the heap object is increased by 1.
Lesson 2
When you write
constructor Create(var AValues: array of Integer);
you need to realise that this, despite the appearance, isn't a dynamic array parameter, but an open array parameter.
If you explicitly want a dynamic array parameter, you need to use such a type explicitly:
constructor Create(AValues: TArray<Integer>);
By definition, TArray<Integer> = array of Integer is a dynamic array of Integers.
Please note that the language only has two types of arrays – static and dynamic; the open array concept is only about function parameters.
If you want to work with dynamic arrays, taking advantage of their nature as reference types, I would suggest you use dynamic array parameters. Then the only thing that is passed to the function (the constructor in this case) is the pointer to the heap object. And the heap object's reference count is increased, of course.
Lesson 3 – An example
var
Arr: TArray<Integer>;
procedure Test(A: TArray<Integer>);
var
i: Integer;
begin
for i := Low(A) to High(A) do
A[i] := 2*A[i];
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
SetLength(Arr, 10);
for i := 0 to High(Arr) do
Arr[i] := i;
Test(Arr);
for i := 0 to High(Arr) do
ShowMessage(Arr[i].ToString);
end;
After SetLength, Arr points to a dynamic array heap object of reference count 1. You can see it in your RAM (press Ctrl+Alt+E, then Ctrl+G and goto Arr[0]). When you enter Test, the reference count is increased to 2 because both Arr and A refer to it. When you leave Test, the reference count is reduced back to 1 because A goes out of scope: now again only Arr refers to it.
Lesson 4
Now, a “gotcha”: if you change the number of elements of a dynamic array, it needs to be reallocated (*). Hence, a new dynamic array heap object is created with a reference count of 1, and the old object has its reference count decreased by 1 (and removed if it becomes zero).
Thus, while the previous example works as expected, the following will not:
var
Arr: TArray<Integer>;
procedure Test(A: TArray<Integer>);
var
i: Integer;
begin
SetLength(A, 5);
for i := Low(A) to High(A) do
A[i] := 2*A[i];
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
SetLength(Arr, 10);
for i := 0 to High(Arr) do
Arr[i] := i;
Test(Arr);
for i := 0 to High(Arr) do
ShowMessage(Arr[i].ToString);
end;
The SetLength will create a new dynamic array heap object with refcount 1 and copy half of the old array into this, put the new address in the local A parameter, and Test will transform this new array, not touching the old one which the global Arr variable points to. The reference count of the original heap object is decreased by one.
However, if you use a var parameter,
procedure Test(var A: TArray<Integer>);
var
i: Integer;
begin
SetLength(A, 5);
for i := Low(A) to High(A) do
A[i] := 2*A[i];
end;
it will work as before. This effectively works with Arr. If we had increased the number of elements, the array would probably have been reallocated and the global Arr variable would have been updated with the new address.
Conclusion
As long as you don't need to reallocate the memory (change the number of elements), Delphi already gives you what you want, because dynamic arrays are reference types. If you do need to reallocate, at least now you know enough of the technical details in order to reason about it.
Update: Hence, the suggestion is to do
type
TTest = class
FData: TArray<Integer>;
constructor Create(AData: TArray<Integer>);
procedure Enlarge;
procedure Shrink;
procedure ShowSum;
end;
{ TTest }
constructor TTest.Create(AData: TArray<Integer>);
begin
FData := AData; // will NOT copy the array, since dynamic arrays are reference types
end;
procedure TTest.Enlarge;
var
i: Integer;
begin
for i := 0 to High(FData) do
FData[i] := 2*FData[i];
end;
procedure TTest.ShowSum;
var
s: Integer;
i: Integer;
begin
s := 0;
for i := 0 to High(FData) do
Inc(s, FData[i]);
ShowMessage(s.ToString);
end;
procedure TTest.Shrink;
var
i: Integer;
begin
for i := 0 to High(FData) do
FData[i] := FData[i] div 2;
end;
To test it:
procedure TForm1.FormCreate(Sender: TObject);
var
MyArray: TArray<Integer>;
t: TTest;
begin
MyArray := [1, 2, 3, 4, 5];
t := TTest.Create(MyArray);
try
t.ShowSum;
t.Enlarge;
t.ShowSum;
t.Shrink;
t.ShowSum;
finally
t.Free;
end;
end;
Footnotes
If you (1) decrease the number of elements and (2) the reference count is 1, typically the data isn't moved in memory. If the reference count is > 1, the data is always moved, because SetLength guarantees that the reference count of its argument is 1 when it returns.

Store value in an array

I am fairly new to Go. I have coded in JavaScript where I could do this:
var x = [];
x[0] = 1;
This would work fine. But in Go, I am trying to implement the same thing with Go syntax. But that doesn't help. I need to have a array with unspecified index number.
I did this:
var x []string
x[0] = "name"
How do I accomplish that?
When you type:
var x []string
You create a slice, which is similar to an array in Javascript. But unlike Javascript, a slice has a set length and capacity. In this case, you get a nil slice which has the length and capacity of 0.
A few examples of how you can do it:
x := []string{"name"} // Creates a slice with length 1
y := make([]string, 10) // Creates a slice with length 10
y[0] = "name" // Set the first index to "name". The remaining 9 will be ""
var z []string // Create an empty nil slice
z = append(z, "name") // Appends "name" to the slice, creating a new slice if required
More indepth reading about slices:
Go slices usage and internals
In JavaScript arrays are dynamic in the sense that if you set the element of an array using an index which is greater than or equal to its length (current number of elements), the array will be automatically extended to have the required size to set the element (so the index you use will become the array's new length).
Arrays and slices in Go are not that dynamic. When setting elements of an array or slice, you use an index expression to designate the element you want to set. In Go you can only use index values that are in range, which means the index value must be 0 <= index < length.
In your code:
var x []string
x[0] = "name"
The first line declares a variable named x of type []string. This is a slice, and its value will be nil (the zero value of all slice types, because you did not provide an initialization value). It will have a length of 0, so the index value 0 is out of range as it is not less that the length.
If you know the length in advance, create your array or slice with that, e.g.:
var arr [3]string // An array with length of 3
var sli = make([]string, 3) // A slice with length of 3
After the above declarations, you can refer to (read or write) values at indicies 0, 1, and 2.
You may also use a composite literal to create and initialize the array or slice in one step, e.g.
var arr = [3]string{"one", "two", "three"} // Array
var sli = []string{"one", "two", "three"} // Slice
You can also use the builtin append() function to add a new element to the end of a slice. The append() function allocates a new, bigger array/slice under the hood if needed. You also need to assign the return value of append():
var x []string
x = append(x, "name")
If you want dynamic "arrays" similar to arrays of JavaScript, the map is a similar construct:
var x = map[int]string{}
x[0] = "name"
(But a map also needs initialization, in the above example I used a composite literal, but we could have also written var x = make(map[int]string).)
You may assign values to keys without having to declare the intent in prior. But know that maps are not slices or arrays, maps typically not hold values for contiguous ranges of index keys (but may do so), and maps do not maintain key or insertion order. See Why can't Go iterate maps in insertion order? for details.
Must read blog post about arrays and slices: Go Slices: usage and internals
Recommended questions / answers for a better understanding:
Why have arrays in Go?
How do I initialize an array without using a for loop in Go?
How do I find the size of the array in go
Keyed items in golang array initialization
Are golang slices pass by value?
Can you please use var x [length]string; (where length is size of the array you want) instead of var x []string; ?
In Go defining a variable like var x=[]int creates a slice of type integer. Slices are dynamic and when you want to add an integer to the slice, you have to append it like x = append(x, 1) (or x = append(x, 2, 3, 4) for multiple).
As srxf mentioned, have you done the Go tour? There is a page about slices.
I found out that the way to do it is through a dynamic array. Like this
type mytype struct {
a string
}
func main() {
a := []mytype{mytype{"name1"}}
a = append(a, mytype{"name 2"})
fmt.Println(a);
}
golang playground link: https://play.golang.org/p/owPHdQ6Y6e

Using arrays in Swift

Just started using Swift and I'm getting pissed at a few elements. First is that most standard stuff are structs rather than objects, which means they're passed in as values rather than pointers as I'm used to. The other thing is that using the optional element system is really annoying.
If I am trying to declare an array without putting anything in it, I declare it like this:
var theArray : [Int]
In order to put anything in it, I would declare it like this:
var theArray : [Int]?
Then add objects as follows:
theArray[someIndex] = someInt
//or
theArray.append(someInt)
However, I get an error. In Java, I could have just initialized an array with a length, which would have given me an a fixed-size array with all 0's.
The problem, summarized in a sentence, is adding elements to Swift arrays that have been initialized without values. How do you do this?
In order to initialize an empty array use:
var theArray : [Int] = []
then add elements by using append method. What you currently did is that you just declared it in the first case non optional and in the second case as an optional variable typed as int array without initializing it.
If you want the array to contain Int types, of course there are many ways to declare that, based on implicit or explicit type inference. These are all valid declarations of an array containing Int types
var array1 = [Int]()
var array2: [Int] = []
var array3 = Array<Int>()
var array4: Array<Int> = []
If you want an array of a certain size, with the values initialized to a certain value you can use, in this example you'll get an Array<Int> with 5 elements, all initialised to 0
var array = Array(count: 5, repeatedValue: Int(0))
Here you go:
var theArray = Array(count:[the length you want], repeatedValue:Int(0))
This will replicate the Java behaviour.

Is there a way to find the index of an item of an array of pointers?

Is there a way to find the index of an item of an array of pointers ?
The code is like this:
type
TArrayItem = record
Field1: string;
Field2: integer;
Field3: boolean;
end;
var
MyItem: TArrayItem;
MyArray: array[1..100] of TArrayItem;
Let's say I take an item from the array (MyItem:=MyArray[20];). After this I sort the array and the item locations are changed; Now, how can I find the new index of MyItem ?
You don't have an array of pointers. Unlike a class, which is a reference type, a record is a value type. The way you have declared the array, the item data is copied whenever you do an assignment. So hen you assign an array item to MyItem, you are making a copy of that item's data, you are not obtaining a pointer to the original item.
In any case, whether you have an array of items or an array of pointers to items, the answer is the same: the only way to find an item in an array is to loop through the array manually, eg:
var
MyItem: TArrayItem;
MyArray: array[1..100] of TArrayItem;
I: Integer;
MyItem := MyArray[20];
// sort the array...
for I := Low(MyArray) to High(MyArray) do
begin
if (MyArray[I].Field1 = MyItem.Field1) and
(MyArray[I].Field2 = MyItem.Field2) and
(MyArray[I].Field3 = MyItem.Field3) then
begin
// item was found at index I...
end;
end;
Otherwise, dynamically allocate your items on the heap and store their pointers in a TList or TList<T>, as they expose IndexOf() methods. And sorting would be faster since you are only moving pointers around, not complete copies of data.
There is no intrinsic mechanic for doing what you ask. You will have to iterate over the array and identify the matching item in order to identify it's position in that array.
In this case you should also note that although your question states that you have an "array of pointers", the code you have posted is not an array of pointers but an array of records, which are value types not references, so this code:
MyItem := MyArray[20];
Does not obtain a reference to the 20th item in MyArray, but rather it creates a copy of it. The code for identifying an item in an array varies quite markedly when you are locating a copy of an item versus a reference to an item.
To find an item in an array of pointers:
var
i, indexOfItem: Integer;
item: ^TArrayItem;
theArray: array[1..100] of ^TArrayItem;
item := theArray[20];
indexOfItem := -1;
for i := Low(theArray) to High(theArray) do
if (theArray[i] = item) then
begin
indexOfItem := i;
BREAK;
end;
To find an item in an array of records you have to test equality of the record fields separately since you cannot compare two records as a whole:
var
i, indexOfItem: Integer;
item: TArrayItem;
theArray: array[1..100] of TArrayItem;
item := theArray[20];
indexOfItem := -1;
for i := Low(theArray) to High(theArray) do
if (theArray[i].Field1 = item.Field1)
and (theArray[i].Field2 = item.Field2)
and (theArray[i].Field3 = item.Field3) then
begin
indexOfItem := i;
BREAK;
end;
You should also be aware that in this latter case there is a built in assumption that no two items will have the same values for the record fields, since only the first matching item will be identified.
NB. The code above is not intended to be robust solutions, only to demonstrate the principles involved.
One last thing to be aware of is the possibility that you have an array of records but have obtained a pointer to some item in that array prior to sorting the array:
var
item: ^TArrayItem;
theArray: array[1..100] of TArrayItem;
item := #theArray[20];
SortTheArray(theArray);
If this is the case then after sorting the array (or any form of manipulation of that array) the value of the item pointer may no longer be reliable at all for a dynamic array, since the array may have moved in memory as a result of the operations performed on it!
Even if the array has not moved, as is currently the case with a static array, the item pointer will point to whichever item is now at the 20th position in that array, not the item that was in that position prior to the sort.

Create a constant array of strings

Is there a way in Delphi declaring an array of strings such as following one?
{'first','second','third'}
In XE7 you can declare a dynamic array constant like this:
const
MyArray: TArray<String> = ['First','Second','Third'];
try this
Const
Elements =3;
MyArray : array [1..Elements] of string = ('element 1','element 2','element 3');
You can use dynamic arrays and try this:
var
FMyArray: TArray<string>;
function MyArray: TArray<string>;
begin
if Length(FMyArray) = 0 then
FMyArray := TArray<string>.Create('One', 'Two', 'Three');
Result := FMyArray;
end;
While this does do a run-time initialization of a dynamic array on the heap, it also shows that Delphi supports a "pseudo-constructor" on dynamic arrays that allow in-place initialization. (NOTE: the above code isn't thread-safe).
Now all you need to do to find out the length of the array, is use the Length() standard function, or to find the allowed index range, use the Low() and High() standard functions.
If you're using an older version of Delphi, replace the TArray with your own dynamic-array string type such as:
type
TStringArray = array of string;
You can do this in a indirect way. Create a function like:
procedure assignStringArray(var rasVelden: ArrayOfString; const asVeldenIn: Array Of String);
var
iLengte, iT1: Integer;
begin
iLengte := Length(asVeldenIn);
SetLength(rasVelden, iLengte);
for iT1 := iLengte-1 downto 0 do
rasVelden[iT1] := asVeldenIn[iT1];
end;
and call this function like:
assignStringArray(asVelden, ['String1', 'String2', 'String3']);
where:
asVelden: ArrayOfString;

Resources