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;
Related
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.
I have a problem with Delphi.
I wrote a function like this:
function MyFunction(arr: array of AnsiString): Boolean;
begin
//code here
end;
And now, when I pass an array of AnsiString directly into function, like this, everything works perfectly:
MyFunction(['one', 'two', 'three']);
But, when I try to store this array like this:
var arr: array of AnsiString;
procedure MyProcedure;
begin
arr[0] := ['one', 'two', 'three'];
MyFunction(arr[0]);
end;
There is a mismatch error.
I'm a beginner with Delphi, but this is really confusing.
Your second example is not functionally identical to the fist example.
The first example is fine. The function takes an open array as an input parameter, and you are constructing a fixed array of strings directly in that parameter, which is perfectly fine. Any array type can be passed to an open array parameter.
In the second example, you are declaring a dynamic array of strings, but you are not allocating any memory for the array, and you are trying to assign its first element (which is a single string) to point at a fixed array of strings. And then you are trying to pass that element (again, a single string) where an array is expected. That is why the code fails to compile.
The correct way to write your procedure would like more like this:
procedure MyProcedure;
var
arr: array of AnsiString;
begin
SetLength(arr, 3);
arr[0] := 'one';
arr[1] := 'two';
arr[2] := 'three';
MyFunction(arr);
end;
Alternatively:
procedure MyProcedure;
var
arr: array of AnsiString;
begin
arr := ['one', 'two', 'three'];
MyFunction(arr);
end;
Alternatively:
type
TAnsiStringArray = array of AnsiString;
procedure MyProcedure;
var
arr: TAnsiStringArray;
begin
arr := TAnsiStringArray.Create('one', 'two', 'three');
MyFunction(arr);
end;
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.
If you work with php you can see the php have associative array (or array width string key) in programing lang.
For example:
$server['hostname'] = 'localhost';
$server['database'] = 'test';
$server['username'] = 'root';
$server['password'] = 'password' ;
// 2d array
$all['myserver']['hostname'] = 'localhost' ;
But can't find any default way to use associative array in delphi.
First I want find default way with out any output component or class .
Second if really I cant find with internal way I force choose output classes only.
I use Delphi XE3 , many thanks for your help.
edit:
I found one class here : http://www.delphipages.com/forum/showthread.php?t=26334
same as php , but any better way?
You can use TDictionary<string,string> from the Generics.Collections unit.
var
Dict: TDictionary<string,string>;
myValue: string;
....
Dict := TDictionary<string,string>.Create;
try
Dict.Add('hostname', 'localhost');
Dict.Add('database', 'test');
//etc.
myValue := Dict['hostname'];
finally
Dict.Free;
end;
And so on and so on.
If you want a dictionary that contains a dictionary, you can do use TDictionary<string, TDictionary<string,string>>.
However, when you do that you'll need to take special care over the lifetime of the dictionary items that are contained in the outer dictionary. You can use TObjectDictionary<K,V> to help manage that for you. You'd create one of these objects like this:
TObjectDictionary<string, TDictionary<string,string>>.Create([doOwnsValues]);
This TObjectDictionary<k,V> operates the same was as a traditional TObjectList with OwnsObjects set to True.
You can use tStrings and tStringList for this purpose, but 2d arrays are out of the scope of these components.
Usage;
var
names : TStrings;
begin
...
names := TStringList.Create;
...
...
names.values['ABC'] := 'VALUE of ABC' ;
...
...
end ;
I had solved the problem that simple way (example):
uses StrUtils;
...
const const_TypesChar : array [0..4] of String =
(
'I',
'F',
'D',
'S',
'B'
);
const const_TypesStr : array [0..4] of String =
(
'Integer',
'Float',
'Datetime',
'String',
'Boolean'
);
...
Value := const_TypesStr[ AnsiIndexStr('S', const_TypesChar) ];
// As an example, after execution of this code Value variable will have 'String' value.
//
Then in program we are using two arrays const_TypesChar and const_TypesStr as one associative array with AnsiIndexStr function.
The plus is that it's simple and that we don't need to change code in different places in program every time when we add elements to our arrays.
Look at ArrayS. You can use associative arrays which stores predefined type of data (integer, string, boolean, float), or any of them. For example, below I define an associative array of floats:
uses ArrayS;
var floats : IFltArray;
floats := CreateArray;
floats['first'] := 0.1;
floats['second'] := 0.2;
writeln( floats['second'] );
And so on.
Updated at 2020-03-15
Zipped source code
Ussage example in Russian
Is there a simple way in delphi to convert an array of strings to a tstringlist?
Once you have created the string list, you can simply call AddStrings().
Or for older versions of Delphi that do not support the AddStrings() overloads that accept arrays, you can roll your own.
function StringListFromStrings(const Strings: array of string): TStringList;
var
i: Integer;
begin
Result := TStringList.Create;
for i := low(Strings) to high(Strings) do
Result.Add(Strings[i]);
end;
Using an open array parameter affords the maximum flexibility for the caller.
For pre-generic versions of Delphi, you can use something like this:
type
TStringArray = array of string;
procedure StringListFromStrings(const StringArray: TStringArray;
const SL: TStringList);
var
// Versions of Delphi supporting for..in loops
s: string;
// Pre for..in version
// i: Integer;
begin
// TStringList should be created and passed in, so it's clear
// where it should be free'd.
Assert(Assigned(SL));
// Delphi versions with for..in support
for s in StringArray do
SL.Add(s);
// Pre for..in versions
// for i := Low(StringArray) to High(StringArray) do
// SL.Add(StringArray[i]);
end;
With recent Delphi version you can call "addStrings" method of a TStrings and TstringList classes like this:
function StringListFromStrings(const Strings: array of string): TStringList;
var
i: Integer;
begin
Result := TStringList.Create;
Result.addStrings(Strings);
end;
Yuo can find documentation about this method at:
https://docwiki.embarcadero.com/Libraries/Sydney/en/System.Classes.TStrings.AddStrings