I'm trying to make an array of an array to get data (which I have previously added) calling it like "GameMap[i, j, k].Items[m].Index" or "GameMap[i, j, k].Count"
My type part in delphi looks like this:
type
TItemRec = record
Index: Integer;
Volume: Integer;
Count: Integer;
Id: string;
end;
TMApRec = record
ID: LongWord;
Count: integer;
Order1: integer;
Order2: integer;
Order3: integer;
Order4: integer;
Order5: integer;
Order6: integer;
Order7: integer;
Order8: integer;
Order9: integer;
Order10: integer;
Items: array[0..9] of TItemRec;
end;
TMap = class
GameMap : array[0..8,0..14,0..$12] of TMapRec;
and for example, if I do now:
procedure TMap.Update;
var
i,j,k,m: integer;
begin
i:=0;
while i < 8 do
begin
j:=0;
while j < 14 do
begin
k:=0;
while k < $12 do
begin
m:= 0;
while m < 10 do
begin
showmessage('asdf');
GameMap[i,j,k].Items[m].Id:= (inttostr(i)+' '+inttostr(j)+' '+inttostr(k)+' '+inttostr(m));
showmessage((GameMap[i,j,k].Items[m].Id));
inc(m);
end;
inc(k);
end;
inc(j);
end;
inc(i);
end;
it will only show the first showmessage('asdf'), but then it crashes
here you have the full code if you want it highlighted http://pastebin.com/xfL94QXU
Thanks again for your time guys
The only way in which your code can produce a run time error is if you failed to instantiate an instance of TMap.
I suspect that the code that calls Update looks like this:
var
Map: TMap;
begin
Map.Update;
end;
This will fail because Map is not initialized. Fix it like this:
var
Map: TMap;
begin
Map := TMap.Create;
try
Map.Update;
finally
Map.Free;
end;
end;
As a more general piece of advice I strongly recommend that you read about how to create a Short, Self Contained, Correct (Compilable), Example.
Declare a type for your Items record first:
type
TItemRec = record
Index: Integer;
Volume: Integer;
Count: Integer;
Id: Integer;
end;
Now use that type in your GameMap:
GameMap : array[0..8,0..14,0..$12] of record
ID: LongWord;
Count: integer;
Order1: integer;
Order2: integer;
Order3: integer;
Order4: integer;
Order5: integer;
Order6: integer;
Order7: integer;
Order8: integer;
Order9: integer;
Order10: integer;
Items: array[0..9] of TItemRec;
end;
Related
Is it possible to convert this:
program RangeLoop;
type
TIndexedProc = reference to procedure(idx: Integer);
var
i: Integer;
p: TIndexedProc;
begin
p := procedure(ix: Integer)
var LocalVar: Integer;
begin
//Do some processing
end;
for i in [2..7] do p(i);
end.
into something like this:
program RangeLoop;
var
i: Integer;
begin
for i in [2..7] do procedure(ix: Integer)
var LocalVar: Boolean;
begin
//Do some processing
end;
end.
?
I know that the last code block is invalid.
In Delphi 10.3 and later, you can use an inline variable inside the loop to hold the anonymous procedure, eg:
program RangeLoop;
var
i: Integer;
begin
for i in [2..7] do
begin
var p := procedure(ix: Integer)
begin
//Do some processing
var LocalVar := ...;
end;
p(i);
end;
end.
Prior to 10.3, what you are asking for is simply not doable the way you want. Defining p above the loop is your best choice, unless you use a standalone procedure, eg:
program RangeLoop;
procedure p(ix: Integer);
var
LocalVar: Integer;
begin
//Do some processing
end;
var
i: Integer;
begin
for i in [2..7] do p(i);
end.
Or, if you can use TParallel.For() instead, if your loop iterations are thread-safe and not dependent on each other's results:
program RangeLoop;
uses
System.Threading;
begin
TParallel.For(2, 7, procedure(ix: Integer)
begin
//Do some processing
end;
);
end.
I have defined a dynamic array type as follows:
TMyIntegerArray = array of integer:
I would like to use an IndexOf function as I would do if it were a TObject's descendant:
var
MyArray : TMyIntegerArray;
i : integer:
begin
//...
i := MyArray.IndexOf(10);
//...
end;
At the moment, the only solution I've found is to write a function who accepts the array and the target value as parameters:
function IndexOf(AArray : TMyIntegerArray; ATargetValue : integer; AOffset : integer = 0);
begin
Result := AOffset;
while(Result < Length(AArray)) do
begin
if(AArray[Result] = ATargetValue)
then Exit;
Result := Result + 1;
end;
Result := -1;
end;
Can TMyIntegerArray type have a function like IndexOf?
Further Informations:
Currently, I'm using Delphi2007 but I'm also interested in knowing if there's any way to add methods to array types in newer Delphi's versions.
In newer versions of Delphi (XE3+), it is possible to implement methods to array types with record helpers:
program ProjectTest;
{$APPTYPE CONSOLE}
Type
TMyArray = array of integer;
TMyArrayHelper = record helper for TMyArray
procedure Print;
function IndexOf(ATargetValue : integer; AOffset : integer = 0): Integer;
end;
procedure TMyArrayHelper.Print;
var
i: Integer;
begin
for i in Self do WriteLn(i); // Use Self for variable reference
end;
function TMyArrayHelper.IndexOf(ATargetValue : integer; AOffset : integer = 0): Integer;
begin
Result := AOffset;
while(Result < Length(Self)) do
begin
if(Self[Result] = ATargetValue)
then Exit;
Result := Result + 1;
end;
Result := -1;
end;
var
myArr : TMyArray;
begin
myArr := [0,1,2]; // A neat way to populate a dynamic array (XE7+)
myArr.Print;
WriteLn(myArr.IndexOf(2));
ReadLn;
end.
Note: You can skip the TMyArray type declaration and use TArray<Integer> for a more relaxed type resolution. As always with record helpers, there can be only one helper attached to a type (and the one that will be used is the one nearest in scope).
This type of helper is called an intrinsic type helper, where the compiler puts an implicit record structure around the type.
Although LU RD showed a direct solution to your question, I am going to add a slightly different approach based on generics. This has the advantage of providing a valid solution for different array types in one place.
For Delphi versions which supports generics, one can adopt the way used in TArray found in System.Generics.Collections. This is a straight forward extension of that class introducing a function IndexOf:
type
TArrayExt = class(TArray)
public
class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index, Count:
Integer): Integer; overload; static;
class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer; overload;
static;
class function IndexOf<T>(const Values: array of T; const Item: T): Integer; overload; static;
end;
class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index,
Count: Integer): Integer;
var
I: Integer;
begin
if (Index < Low(Values)) or ((Index > High(Values)) and (Count > 0))
or (Index + Count - 1 > High(Values)) or (Count < 0)
or (Index + Count < 0) then
raise EArgumentOutOfRangeException.CreateRes(#SArgumentOutOfRange);
if Count = 0 then
begin
Exit(-1);
end;
for I := Index to Index + Count - 1 do begin
if Comparer.Equals(Item, Values[I]) then begin
Exit(I);
end;
end;
Result := -1;
end;
class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer;
begin
Result := IndexOf<T>(Values, Item, Comparer, Low(Values), Length(Values));
end;
class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T): Integer;
begin
result := IndexOf<T>(Values, Item, TEqualityComparer<T>.Default, Low(Values), Length(Values));
end;
A simple use case could look like this:
procedure Main;
var
arr: TArray<Integer>;
N: Integer;
begin
arr := TArray<Integer>.Create(5, 7, 3, 4, 2);
repeat
Readln(N);
N := TArrayExt.IndexOf(arr, N);
Writeln(N);
until false;
end;
For Delphi 2009 and above you can use a generic list:
uses System.Generics.Collections;
var
MyArray : TList<integer>;
i : integer;
begin
MyArray := TList<integer>.Create;
MyArray.Add(3);
MyArray.Add(7);
MyArray.Add(10);
i := MyArray.IndexOf(10);
end;
In Delphi 2007 you might use a custom record:
type
TMyArray = record
private
TheArray : array of integer;
public
procedure Add(Value : integer);
function IndexOf(Value : integer) : integer;
function Length : integer;
end;
procedure TMyArray.Add(Value : integer);
var
i : integer;
begin
i := length(TheArray);
setlength(TheArray,i+1);
TheArray[i] := Value;
end;
function TMyArray.IndexOf(Value : integer) : integer;
var
i : integer;
begin
for i := 0 to length(TheArray)-1 do
begin
if TheArray[i] = Value then
begin
Result := i;
exit;
end;
end;
Result := -1;
end;
function TMyArray.Length : integer;
begin
Result := length(TheArray);
end;
procedure MyFunction;
var
MyArray : TMyArray;
i : integer;
begin
MyArray.Add(3);
MyArray.Add(7);
MyArray.Add(10);
i := MyArray.IndexOf(10);
end;
I think this will look like 'do my homework' kind of a question, but I'm still at the 'copy code, use it and try to understand it' phase, and this is the most active thing I know of for posting questions of this theme.
I have a record:
type
Card = record
Name: string;
Up,Right,Left,Down,Mark: Single;
IDNumber: Integer;
end;
And array of that record:
var
ArrayCard: array of Card;
And I wanted to know how can a dynamic array of this kind be stored/loaded to/from a file.
Tried using this piece of code: http://www.pascalgamedevelopment.com/showthread.php?6319-save-load-a-dynamic-array
like this:
Procedure TMainFrom.WriteMyData;
Var
FS : TFileStream;
I,iSize : Integer;
TmpPath: string;
Begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.dat');
FS := TFileStream.Create(TmpPath, fmOpenWrite);
iSize:= Length(ArrayCard);
FS.WriteBuffer(iSize,SizeOf(iSize));
For I := 0 To iSize - 1 Do
FS.WriteBuffer(ArrayCard[I],SizeOf(Card));
FS.Free;
End;
An it seems to work so far, but then I try to load it like this:
Procedure TMainFrom.ReadMyData;
Var
FS : TFileStream;
I,iSize : Integer;
TmpPath: string;
TempCard : Card;
Begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.dat');
FS := TFileStream.Create(TmpPath, fmOpenRead);
FS.ReadBuffer(iSize,SizeOf(iSize));
SetLength(ArrayCard,iSize);
For I := 0 To iSize - 1 Do
Begin
FS.ReadBuffer(TempCard,SizeOf(Card));
ArrayCard[I] := TempCard; //It Breaks Here...The Watch List: TempCard Inaccessible value
End;
FS.Free;
End;
And I get a Exception EAccessViolation in module...
Then I also tried something like this: delphi Save and Load dynamic array
It loads the array with the correct amount of items, but they are all empty or blank:
procedure TMainFrom.SaveCardsToFile;
var
Stream: TStream;
L: Integer;
TmpPath: string;
ICount: Integer;
strm: TMemoryStream;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmCreate);
L:= Length(ArrayCard);
Stream.WriteBuffer(L, SizeOf(L));
Stream.WriteBuffer(Pointer(ArrayCard)^, L * SizeOf(Card));
Stream.Free;
end;
procedure TMainFrom.LoadCardsFromFile;
var
Stream: TStream;
L: LongWord;
TmpPath: string;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmOpenRead);
Stream.ReadBuffer(L, SizeOf(L));
SetLength(ArrayCard, L);
Stream.ReadBuffer(Pointer(ArrayCard)^, L * SizeOf(Card));
Stream.Free;
end;
Do not use buffer operations with records which contains "normal" strings. Instead of shortstring, string is only pointer to string content. So, your code saves and loads only pointer to string content, not string. You can get Access Violation if loaded value points to unreachable memory.
Change your code to separately save and load variables in record, like this:
type
TmyRec = record
str: string;
i: integer;
procedure SaveToStream(Stream: TStream);
procedure LoadFromStream(Stream: TStream);
end;
{ myRec }
procedure TmyRec.LoadFromStream(Stream: TStream);
var
strLen: integer;
strBuf: TBytes;
begin
Stream.Read(strLen, SizeOf(Integer));
SetLength(strBuf, strLen);
Stream.Read(strBuf, strLen);
str:=TEncoding.UTF8.GetString(strBuf);
Stream.Read(i, SizeOf(Integer));
end;
procedure TmyRec.SaveToStream(Stream: TStream);
var
strBuf: TBytes;
strLen: integer;
begin
// direct set encoding type helps to avoid problems with different platforms.
// for example, Windows uses UCS2, Android and iOS - UTF8 encoding
strBuf:=TEncoding.UTF8.GetBytes(str);
strLen:=Length(strBuf);
Stream.Write(strLen, SizeOf(Integer));
Stream.Write(strBuf, strLen);
Stream.Write(i, SizeOf(Integer));
end;
Update:
do you read about generics? Instead of dynamic array, you can use TList and load/save records in it:
type
TmyRecList = class(TList<TmyRec>)
public
procedure SaveToStream(Stream: TStream);
procedure LoadFromStream(Stream: TStream);
end;
{ TmyRecList }
procedure TmyRecList.LoadFromStream(Stream: TStream);
var
Len: integer;
i: Integer;
rec: TmyRec;
begin
Clear;
Stream.Read(Len, SizeOf(integer));
for i := 0 to Len-1 do
begin
Rec.LoadFromStream(Stream);
Add(rec);
end;
end;
procedure TmyRecList.SaveToStream(Stream: TStream);
var
i: Integer;
begin
Stream.Write(Count, SizeOf(Integer));
for i := 0 to Count-1 do
Items[i].SaveToStream(Stream);
end;
procedure THeaderFooterForm.FormCreate(Sender: TObject);
var
Stream: TStream;
rec: TmyRec;
recList: TmyRecList;
begin
Stream:=TMemoryStream.Create;
try
recList:=TmyRecList.Create;
try
rec.str:='sample text';
rec.i:=123;
recList.Add(rec);
rec.str:='another text';
rec.i:=234;
recList.Add(rec);
recList.SaveToStream(Stream);
Stream.Seek(0, soBeginning);
recList.LoadFromStream(Stream);
ShowMessage('this is str value in second record: ' + recList[1].str);
finally
recList.Free;
end;
finally
Stream.Free;
end;
With some help, I have managed to remake my code to work properly.
Firstly I needed to make the string something like string[20] but that couldn't compile for android so I modified my record to use array of char like this:
type
EventString= array [0..20] of Char;
Card = record
Name: EventString; //string[20]
Up,Right,Left,Down,Mark: Single;
IDNumber: Integer;
end;
Then I modified my Saving/Loading procedures to use TFileStream instead of TStream and a for loop:
procedure TMainFrom.SaveCardsToFile;
var
Stream: TFileStream;
L: Integer;
TmpPath: string;
n: Integer;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmCreate);
L:= Length(ArrayCard)-1;
for n:=0 to L do
begin
Stream.WriteBuffer(ArrayCard[n], SizeOf(Card)); //Saving
end;
Stream.Free;
end;
procedure TMainFrom.LoadCardsFromFile;
var
Stream: TFileStream;
L: LongWord;
TmpPath: string;
n: Integer;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmOpenRead);
SetLength(ArrayCard, Round(Stream.Size/SizeOf(Card)));
L:= Length(ArrayCard)-1;
for n:=0 to L do
begin
Stream.ReadBuffer(ArrayCard[n], SizeOf(Card)); //Loading
ListCard.Items.Add(ArrayCard[n].Name); //Just adds card names to a listbox
end;
Stream.Free;
end;
And now it works just fine.
I have function to search for TItem (my own class) in array of TItem.
function IndexOfArray(const Value: TItem; Things: array of TItem): integer;
var
i: integer;
begin
Result := -1;
for i := Low(Things) to High(Things) do
if Value = Things[i] then
begin
Result := i;
Break;
end;
end;
It's working for array of TItem. But I want to use it with TItem or TIamge or TLabel. I tried TObject or TComponent as input parameters of this method, but compiler shouts:
E2010 Incompatible types: 'array of TComponent' and 'Dynamic array'
The array of TLabel/TImage/TItem MUST be dynamic. Any ideas please?
TArray.BinarySearch does that for you.
if TArray.BinarySearch<TLabel>(Labels,LabelLoaded,index) then
// Index holds the index of the found item
Note: BinarySearch requires that the array be sorted.
If you just want to compare the pointer value of the objects, here is an example:
Type
TMyArray = record
class function IndexOfArray<T:Class>(const value: T; const Things: array of T): Integer; static;
end;
class function TMyArray.IndexOfArray<T>(const value: T; const Things: array of T): Integer;
var
i: Integer;
begin
for i := 0 to High(Things) do
if value = Things[i] then
Exit(i);
Result := -1;
end;
If you want to write your own bicycle you can try something like this:
type
myAr = array of TObject;
. . .
function IndexOfArray(const Value: TObject; Things: myAr): integer;
var
i: integer;
begin
Result := -1;
for i := Low(Things) to High(Things) do
if (Things[i] is Value.ClassType) and // don't sure that this is nessesary
(Value = Things[i]) then
begin
Result := i;
Break;
end;
end;
procedure someProc;
var
ar : myAr;
lbl : TLabel;
i : integer;
begin
SetLength(ar, 10);
for I := Low(ar) to High(ar) do
ar[i] := TLabel.Create(self);
lbl := TLabel.Create(Self);
i := IndexOfArray(lbl, ar);
end;
Also you can use standard System.Generics.Collections.TArray.BinarySearch class.
The compiler allows me to do the following:
procedure MyProc(const ADynData: array of string);
or
procedure MyProc(const ADynData: TStringDynArray);
and pass arbitrary data like so:
MyProc(['Data1', 'Data2']);
However, won't allow
function MyFunc: TStringDynArray;
....
function MyFunc: TStringDynArray;
begin
Result := ['Data1', 'Data2'];
end;
or
function MyFunc: TStringDynArray;
const
CDynData: array[0..1] of string = ('Data1', 'Data2');
begin
Result := CDynData;
end;
Why is this? Isn't this technically the same thing?
For these particular scenarios what is the recommended (and most efficient) way of returning an arbitrary array of string?
No, it's not the same thing. In
procedure MyProc(const ADynData: array of string);
the argument is an open array parameter, which is not the same thing as an 'ordinary' dynamic array. The [..] syntax can only be used to create open arrays in open array parameters of functions. (Otherwise, [..] is used to specify sets in code, such as Font.Style := [fsBold, fsItalic]. But sets can only have ordinal types as their 'base types', so there is still no such thing as 'set of string'.)
In other words, it is not possible to write a dynamic array in code like you try in your second code snippet,
function MyFunc: TStringDynArray;
begin
result := ['Data1', 'Data2']; // Won't work.
end;
However, in new versions of Delphi, it is almost possible:
type
TStringDynArray = array of string;
function MyFunc: TStringDynArray;
begin
result := TStringDynArray.Create('A', 'B');
end;
Finally,
function MyFunc: TStringDynArray;
const
CDynData: array[0..1] of string = ('Data1', 'Data2');
begin
result := CDynData;
end;
won't work because TStringDynArray is a dynamic array, while CDynData is a static array, which are two different fundamental types.
This construct
['string1', 'string2']
is known as an open array constructor. From the documentation:
Open array constructors allow you to construct arrays directly within function and procedure calls.
They can be passed only as open array parameters or variant open array parameters.
So, you cannot use an open array constructor to create a function return value.
If you have a fixed number of elements in the array that you need to return, you can use a dynamic array constructor:
Result := TStringDynArray.Create('string1', 'string2');
However, this will not work for a variable number of elements. Now, I know that the example in your question only has a constant number of elements in the array. But I'm sure you'll encounter situations where you need more flexibility than a dynamic array constructor can provide.
If you wish to create a copy of an existing dynamic array and return that, use Copy.
Result := Copy(SomeOtherDynamicArray);
This breaks down when you have an open array at hand. You cannot pass an open array to Copy. Personally I think this is rather a shame since open array parameters are so exceptionally flexible and useful that I'd like to see as much RTL support for them as possible.
So, you end up having to write helper functions for those situations. You can write a dedicated helper for each array type, but that becomes somewhat tiresome. That's where generics come in handy. I have a helper class for the purpose. Here's the relevant extract:
type
TArray = class(Generics.Collections.TArray)
....
class function Copy<T>(const Source: array of T): TArray<T>; overload; static;
....
end;
class function TArray.Copy<T>(const Source: array of T): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Length(Source));
for i := 0 to high(Result) do begin
Result[i] := Source[i];
end;
end;
Now, this works with your string arrays, but also with any other type. Call it like this:
Result := TArray.Copy<string>(SomeStringOpenArray);
A critical point to make is that we are using the generic version of the dynamic array, TArray<string> rather than TStringDynArray. It's essential that you do that if you want to use generics seriously. That's because TStringDynArray is not assignment compatible with TArray<string> or indeed any other type declared as array of string. It pays dividends to change your code base to use TArray<T> throughout.
Just in case anyone is interested in the rest of this helper class, here it is:
type
TArray = class(Generics.Collections.TArray)
private
class function Comparison<T>(SortType: TSortType): TComparison<T>; static;
class function Comparer<T>(const Comparison: TComparison<T>): IComparer<T>; static;
public
class procedure Swap<T>(var Left, Right: T); static;
class procedure Reverse<T>(var Values: array of T); static;
class function Reversed<T>(const Values: array of T): TArray<T>; static;
class function Contains<T>(const Values: array of T; const Item: T; out ItemIndex: Integer): Boolean; overload; static;
class function Contains<T>(const Values: array of T; const Item: T): Boolean; overload; static;
class function IndexOf<T>(const Values: array of T; const Item: T): Integer; static;
class function Sorted<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer): Boolean; overload; static;
class function Sorted<T>(var Values: array of T; SortType: TSortType): Boolean; overload; static;
class function Sorted<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Boolean; overload; static;
class function Sorted<T>(var Values: array of T; const Comparison: TComparison<T>): Boolean; overload; static;
class function Sorted<T>(GetValue: TFunc<Integer,T>; const Comparison: TComparison<T>; Index, Count: Integer): Boolean; overload; static;
class procedure Sort<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer); overload; static;
class procedure Sort<T>(var Values: array of T; SortType: TSortType); overload; static;
class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer); overload; static;
class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>); overload; static;
class function Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>; overload; static;
class function Copy<T>(const Source: array of T): TArray<T>; overload; static;
class procedure Move<T>(const Source: array of T; var Dest: array of T; Index, Count: Integer); overload; static;
class procedure Move<T>(const Source: array of T; var Dest: array of T); overload; static;
class function Concatenated<T>(const Source1, Source2: array of T): TArray<T>; overload; static;
class function Concatenated<T>(const Source: array of TArray<T>): TArray<T>; overload; static;
class procedure Initialise<T>(var Values: array of T; const Value: T); static;
class procedure Zeroise<T>(var Values: array of T); static;
class function GetHashCode<T>(const Values: array of T): Integer; overload; static;
class function GetHashCode<T>(Values: Pointer; Count: Integer): Integer; overload; static;
end;
class function TArray.Comparison<T>(SortType: TSortType): TComparison<T>;
var
DefaultComparer: IComparer<T>;
begin
DefaultComparer := TComparer<T>.Default;
Result :=
function(const Left, Right: T): Integer
begin
case SortType of
stIncreasing:
Result := DefaultComparer.Compare(Left, Right);
stDecreasing:
Result := -DefaultComparer.Compare(Left, Right);
else
RaiseAssertionFailed(Result);
end;
end;
end;
class function TArray.Comparer<T>(const Comparison: TComparison<T>): IComparer<T>;
begin
Result := TComparer<T>.Construct(Comparison);
end;
class procedure TArray.Swap<T>(var Left, Right: T);
var
temp: T;
begin
temp := Left;
Left := Right;
Right := temp;
end;
class procedure TArray.Reverse<T>(var Values: array of T);
var
bottom, top: Integer;
begin
bottom := 0;
top := high(Values);
while top>bottom do begin
Swap<T>(Values[bottom], Values[top]);
inc(bottom);
dec(top);
end;
end;
class function TArray.Reversed<T>(const Values: array of T): TArray<T>;
var
i, j, Count: Integer;
begin
Count := Length(Values);
SetLength(Result, Count);
j := Count-1;
for i := 0 to Count-1 do begin
Result[i] := Values[j];
dec(j);
end;
end;
class function TArray.Contains<T>(const Values: array of T; const Item: T; out ItemIndex: Integer): Boolean;
var
DefaultComparer: IEqualityComparer<T>;
Index: Integer;
begin
DefaultComparer := TEqualityComparer<T>.Default;
for Index := 0 to high(Values) do begin
if DefaultComparer.Equals(Values[Index], Item) then begin
ItemIndex := Index;
Result := True;
exit;
end;
end;
ItemIndex := -1;
Result := False;
end;
class function TArray.Contains<T>(const Values: array of T; const Item: T): Boolean;
var
ItemIndex: Integer;
begin
Result := Contains<T>(Values, Item, ItemIndex);
end;
class function TArray.IndexOf<T>(const Values: array of T; const Item: T): Integer;
begin
Contains<T>(Values, Item, Result);
end;
class function TArray.Sorted<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer): Boolean;
begin
Result := Sorted<T>(Values, Comparison<T>(SortType), Index, Count);
end;
class function TArray.Sorted<T>(var Values: array of T; SortType: TSortType): Boolean;
begin
Result := Sorted<T>(Values, Comparison<T>(SortType));
end;
class function TArray.Sorted<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Boolean;
var
i: Integer;
begin
for i := Index+1 to Index+Count-1 do begin
if Comparison(Values[i-1], Values[i])>0 then begin
Result := False;
exit;
end;
end;
Result := True;
end;
class function TArray.Sorted<T>(var Values: array of T; const Comparison: TComparison<T>): Boolean;
begin
Result := Sorted<T>(Values, Comparison, 0, Length(Values));
end;
class function TArray.Sorted<T>(GetValue: TFunc<Integer, T>; const Comparison: TComparison<T>; Index, Count: Integer): Boolean;
var
i: Integer;
begin
for i := Index+1 to Index+Count-1 do begin
if Comparison(GetValue(i-1), GetValue(i))>0 then begin
Result := False;
exit;
end;
end;
Result := True;
end;
class procedure TArray.Sort<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer);
begin
Sort<T>(Values, Comparison<T>(SortType), Index, Count);
end;
class procedure TArray.Sort<T>(var Values: array of T; SortType: TSortType);
begin
Sort<T>(Values, SortType, 0, Length(Values));
end;
class procedure TArray.Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer);
begin
if not Sorted<T>(Values, Comparison, Index, Count) then begin
Sort<T>(Values, Comparer<T>(Comparison), Index, Count);
end;
end;
class procedure TArray.Sort<T>(var Values: array of T; const Comparison: TComparison<T>);
begin
Sort<T>(Values, Comparison, 0, Length(Values));
end;
class function TArray.Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Count);
for i := 0 to high(Result) do begin
Result[i] := Source[i+Index];
end;
end;
class function TArray.Copy<T>(const Source: array of T): TArray<T>;
var
i: Integer;
begin
SetLength(Result, Length(Source));
for i := 0 to high(Result) do begin
Result[i] := Source[i];
end;
end;
class procedure TArray.Move<T>(const Source: array of T; var Dest: array of T; Index, Count: Integer);
var
i: Integer;
begin
for i := 0 to Count-1 do begin
Dest[i] := Source[i+Index];
end;
end;
class procedure TArray.Move<T>(const Source: array of T; var Dest: array of T);
var
i: Integer;
begin
for i := 0 to high(Source) do begin
Dest[i] := Source[i];
end;
end;
class function TArray.Concatenated<T>(const Source1, Source2: array of T): TArray<T>;
var
i, Index: Integer;
begin
SetLength(Result, Length(Source1)+Length(Source2));
Index := 0;
for i := low(Source1) to high(Source1) do begin
Result[Index] := Source1[i];
inc(Index);
end;
for i := low(Source2) to high(Source2) do begin
Result[Index] := Source2[i];
inc(Index);
end;
end;
class function TArray.Concatenated<T>(const Source: array of TArray<T>): TArray<T>;
var
i, j, Index, Count: Integer;
begin
Count := 0;
for i := 0 to high(Source) do begin
inc(Count, Length(Source[i]));
end;
SetLength(Result, Count);
Index := 0;
for i := 0 to high(Source) do begin
for j := 0 to high(Source[i]) do begin
Result[Index] := Source[i][j];
inc(Index);
end;
end;
end;
class procedure TArray.Initialise<T>(var Values: array of T; const Value: T);
var
i: Integer;
begin
for i := 0 to high(Values) do begin
Values[i] := Value;
end;
end;
class procedure TArray.Zeroise<T>(var Values: array of T);
begin
Initialise<T>(Values, Default(T));
end;
{$IFOPT Q+}
{$DEFINE OverflowChecksEnabled}
{$Q-}
{$ENDIF}
class function TArray.GetHashCode<T>(const Values: array of T): Integer;
// see http://stackoverflow.com/questions/1646807 and http://stackoverflow.com/questions/11294686
var
Value: T;
EqualityComparer: IEqualityComparer<T>;
begin
EqualityComparer := TEqualityComparer<T>.Default;
Result := 17;
for Value in Values do begin
Result := Result*37 + EqualityComparer.GetHashCode(Value);
end;
end;
class function TArray.GetHashCode<T>(Values: Pointer; Count: Integer): Integer;
// see http://stackoverflow.com/questions/1646807 and http://stackoverflow.com/questions/11294686
var
Value: ^T;
EqualityComparer: IEqualityComparer<T>;
begin
EqualityComparer := TEqualityComparer<T>.Default;
Result := 17;
Value := Values;
while Count>0 do begin
Result := Result*37 + EqualityComparer.GetHashCode(Value^);
inc(Value);
dec(Count);
end;
end;
{$IFDEF OverflowChecksEnabled}
{$Q+}
{$ENDIF}
The problem with
function MyFunc: TStringDynArray;
begin
Result := ['Data1', 'Data2'];
end;
is that ['Data1', 'Data2'] is interpreted as a set.
I sometimes use the following convenience function (but usually not in performance-critical sections):
function MakeStringArray(const Strings: array of string): TStringDynArray;
var
i: Integer;
begin
SetLength(Result, Length(Strings));
for i := Low(Strings) to High(Strings) do
Result[i] := Strings[i];
end {MakeStringArray};
Using that, you could rewrite your first example as follows:
function MyFunc: TStringDynArray;
....
function MyFunc: TStringDynArray;
begin
Result := MakeStringArray(['Data1', 'Data2']);
end;
But since you're using XE3, you're better off using TStringDynArray.Create, like Andreas Rejbrand suggests.