How would I code this so that if an element equals a certain value it would display a message, but if ALL elements inside that array aren't equal to that value, then it would output 'None'?
I've tried
for i := 0 to high(array) do
begin
if (array[i].arrayElement = value) then
begin
WriteLn('A message');
end;
end;
That bit works, but I don't know how to do the check all bit. I had this:
if (array[i].arrayElement to array[high(array)].arrayElement <> value) then
begin
WriteLn('None');
end;
But it didn't allow me to use "to"
It's clearest to write a helper function for this:
function ArrayContains(const arr: array of Integer; const value: Integer): Boolean;
var
i: Integer;
begin
for i := Low(arr) to High(arr) do
if arr[i] = value then
begin
Result := True;
Exit;
end;
Result := False;
end;
Or using for/in:
function ArrayContains(const arr: array of Integer; const value: Integer): Boolean;
var
item: Integer;
begin
for item in arr do
if item = value then
begin
Result := True;
Exit;
end;
Result := False;
end;
Then you call it like this:
if not ArrayContains(myArray, myValue) then
Writeln('value not found');
Related
I'm trying to conditionally assign a local open array variable in order it points to a const open array parameter:
uses
System.Generics.Collections,
System.Generics.Defaults;
type
TArray = class(System.Generics.Collections.TArray)
public
class function SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean; overload; static;
end;
...
class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
ArrA : TArray<T>;
ArrB : TArray<T>;
i : integer;
begin
//checking sizes
if(Length(AValuesA) <> Length(AValuesB)) then
begin
Result := False;
Exit;
end;
if(AOrderMatters) then
begin
//I don't need to change the arrays, so I could directly point to the open array parameters
ArrA := AValuesA;
ArrB := AValuesB;
end else
begin
//copying to local arrays
SetLength(ArrA, Length(AValuesA));
TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
SetLength(ArrB, Length(AValuesB));
TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));
//sorting local arrays
TArray.Sort<T>(ArrA);
TArray.Sort<T>(ArrB);
end;
//comparing elements
i := 0;
while(i < Length(ArrA)) do
begin
if(not AComparer.Equals(ArrA[i], ArrB[i])) then
begin
Result := False;
Exit;
end;
Inc(i);
end;
Result := True;
end;
On compiling, it raises E2010 error at:
//I don't need to change the arrays, so I could directly point to the open array parameters
ArrA := AValuesA;
ArrB := AValuesB;
E2010 Incompatible types: 'Dynamic array' and 'array of T'
I've tried the following:
ArrA := #AValuesA;
ArrB := #AValuesB;
It compiles but then it raises AV exception at runtime (and most of all, I don't know if it would be a safe approach).
Here is my test application code:
uses
System.Generics.Defaults;
procedure TForm1.FormCreate(Sender: TObject);
var
ArrA : array of string;
ArrB : array of string;
begin
SetLength(ArrA, 2);
ArrA[0] := 'hello';
ArrA[1] := 'world';
SetLength(ArrB, 2);
ArrB[0] := 'world';
ArrB[1] := 'hello';
if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, True)) then
ShowMessage('Same values and same order')
else if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, False)) then
ShowMessage('Same values but different order')
else
ShowMessage('Different values');
end;
What you are trying to do is not possible because open arrays and dynamic arrays are not compatible in the way that your code attempts.
There is an easy way to achieve what you want though, using a simple recursion. Like this:
class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
ArrA : TArray<T>;
ArrB : TArray<T>;
i : integer;
begin
//checking sizes
if Length(AValuesA) <> Length(AValuesB) then
begin
Result := False;
Exit;
end;
if not AOrderMatters then
begin
//copying to local arrays
SetLength(ArrA, Length(AValuesA));
TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
SetLength(ArrB, Length(AValuesB));
TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));
//sorting local arrays
TArray.Sort<T>(ArrA);
TArray.Sort<T>(ArrB);
Result := SameValues<T>(ArrA, ArrB, AComparer, True);
Exit;
end;
//comparing elements
for i := 0 to High(AValuesA) do
begin
if not AComparer.Equals(AValuesA[i], AValuesB[i]) then
begin
Result := False;
Exit;
end;
end;
Result := True;
end;
I have the following JSON structure, calling it from web:
[
{
"Code":"31212",
"Desc":"JOHN",
"DOYDESC":"MADRID",
"Street":"Str41",
"StreetNo":"86"
},
{
"Code":"30214",
"Desc":"GEORGE",
"DOYDESC":"NEW YORK",
"Street":"Str3",
"StreetNo":null
},
{
"Code":"09215",
"Desc":"MARY",
"DOYDESC":"PARIS",
"Street":"Str3",
"StreetNo":"22"
},
{
"Code":"10217",
"Desc":"MEGAN",
"DOYDESC":"ROME",
"Street":"Str4",
"StreetNo":null
}
]
I want the user to give a number in Edit.Text, and then search the values of Code to find if the given value exists.
Is there a way to create an array named Code with the values from the JSON?
For example, to create something like this:
Code = ["31212","30214","09215","10217"]
and then make the search? Or, is there another way to find a specific value of Code from the JSON? I tried this so far:
procedure Tlog_form.Button2Click(Sender: TObject);
var
jso : TJsonObject;
jsv,jsval : TJsonValue;
jsa,jsarr : TJsonArray;
data,str : string;
i,j,user : integer;
jsp : TJsonPair;
arr : array of string;
begin
try
data := GetURLAsString('http://...');
except
on E: exception do
end;
try
jsv := TJSONObject.ParseJSONValue(data);
try
jsa := jsv as TJSONArray;
try
for I := 0 to jsa.Size - 1 do
begin
jso := jsa.Get(i) as TJsonObject;
if jso.ToString = 'Code' then
begin
str := jso.GetValue('Code').ToString;
if Edit1.Text = str then
begin
ShowMessage('found it');
end;
end;
end;
finally
end;
finally
jsv.Free;
end;
except
on E: exception do
end;
end;
When I debug, it doesn't get any error. but it still doesn't work.
You are converting each TJSONObject in the array to a string, and then comparing that to your TEdit text. Which will obviously not match. You need to compare only the Code field of each TJSONObject, which you would be doing if you remove the if jso.ToString = 'Code' then check completely, eg:
procedure Tlog_form.Button2Click(Sender: TObject);
var
jsv : TJSONValue;
jsa : TJSONArray;
jso : TJSONObject;
data, str, code : string;
I : integer;
begin
str := Edit1.Text;
try
data := GetURLAsString('http://...');
jsv := TJSONObject.ParseJSONValue(data);
if jsv <> nil then
try
jsa := jsv as TJSONArray;
for I := 0 to jsa.Size - 1 do
begin
jso := jsa.Get(I) as TJSONObject;
code := jso.GetValue('Code').ToString;
if str = code then
begin
ShowMessage('found it');
end;
end;
finally
jsv.Free;
end;
except
end;
end;
I'm not sure I understand you very well, but if you try TALJsonDocument (https://github.com/Zeus64/alcinoe), you have this function:
Function ALFindJsonNodeByTextChildNodeValue(const JsonNode:TalJsonNode;
Const ChildNodeName: AnsiString;
Const ChildNodeValue : AnsiString;
Const Recurse: Boolean = False): TALJsonNode;
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 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.
I have
playerIds : Array[0..500] of string;
function isPlayerMob(check : string): boolean;
var
i : integer;
begin
for i := 0 to 500 do
begin
if ((playerIds[i] <> '') and (playerIds[i] = check)) then
begin
result := true;
end;
end;
result := false;
end;
I get the Warning
Hint: Value assigned to 'isPlayerMob' never used
Can somebody tell me how to fix this?
The error is for the
result := true;
As others have told you, the value your loop is assigning to Result is being discarded because you are not exiting the function before the final assignment to Result, so it does not matter what the loop assigns.
You could assign the Result with an initial value and then reassign it as needed, or you could simply Exit after you have assigned your desired value:
function isPlayerMob(check : string): boolean;
var
i : integer;
begin
for i := 0 to 500 do
begin
if ((playerIds[i] <> '') and (playerIds[i] = check)) then
begin
Result := True;
Exit; // <-- add this
end;
end;
Result := False; // <-- only performed if the loop does not find a match
end;
Or, if you are using a recent Delphi version:
function isPlayerMob(check : string): boolean;
var
i : integer;
begin
for i := 0 to 500 do
begin
if ((playerIds[i] <> '') and (playerIds[i] = check)) then
Exit(True); // <-- sets Result and exits at the same time
end;
Result := False; // <-- only performed if the loop does not find a match
end;
This hint is because your always are assigning the false value to the function. no matter if the value if found in the loop.
try this
function isPlayerMob(const check : string): boolean;
var
i : integer;
begin
result := false;
for i := 0 to 500 do
if ((playerIds[i] <> '') and (playerIds[i] = check)) then
begin
result := true;
break;
end;
end;
Your function as written will always execute the final Result := false; line, and therefore the value assigned inside the loop will always be discarded.
Change your function instead to initialize the result first:
function isPlayerMob(check : string): boolean;
var
i : integer;
begin
Result := false;
for i := 0 to 500 do
begin
if ((playerIds[i] <> '') and (playerIds[i] = check)) then
begin
Result := true;
Exit;
end;
end;
end;
In Delphi 2009 and higher, the Result := True; Exit; lines can be replaced with simply Exit(True); instead.