Delete strings from TStringList - string

I have a List Box or a List View with items. And I have a String List with the same items (strings) as the List Box/List View. I want to delete all selected items in the List Box/List View from the String List.
How to do?
for i:=0 to ListBox.Count-1 do
if ListBox.Selected[i] then
StringList1.Delete(i); // I cannot know exactly an index, other strings move up

for i := ListBox.Count - 1 downto 0 do
if ListBox.Selected[i] then
StringList1.Delete(i);

The trick is to run the loop in reverse order:
for i := ListBox.Count-1 downto 0 do
if ListBox.Selected[i] then
StringList1.Delete(i);
This way, the act of deleting an item only changes the indices of elements later in the list, and those elements have already been processed.

Solution provided by Andreas and David assumes that the strings are exactly in same order in both ListBox and StringList. This is good assumption as you don't indicate otherwise, but in case it is not true you can use StringList's IndexOf method to find the index of the string (if the StringList is sorted, use Find instead). Something like
var x, Idx: Integer;
for x := ListBox.Count - 1 downto 0 do begin
if ListBox.Selected[x] then begin
idx := StringList.IndexOf(ListBox.Items[x]);
if(idx <> -1)then StringList.Delete(idx);
end;
end;

How about doing it the other way round (adding instead of deleting)?
StringList1.Clear;
for i:=0 to ListBox.Count-1 do
if not ListBox.Selected[i] then StringList1.Add(ListBox.Items(i));

Related

How to access object using string value

I have a file that I wanted to read and apply the new values to an object. How do I use the string name to look for the desired object and update the value. Do note that content in file.txt can be over 1000 lines long and order will changes so I can't have if statement to check each condition.
file.txt
>A 1
>B 2
>D 5
.
desired result
Sample.A := 1;
Sample.B := 2;
Sample.D := 5;
One way is to declare your object to be an array indexed by an enumeration type whose values are the identifiers. For instance, if the identifiers are the symbols A through Z you might define the array as:
subtype Index is Character range 'A'..'Z';
type Collection is array(Index) of Integer;
Idx : Index;
Value : Integer;
The_Collection : Collection;
while not End_Of_File(Input_File) loop
Get(Idx);
Get(Value);
Skip_Line;
The_Collection(Idx) := Value;
end loop;
Of course you will need to "with" the appropriate I/O packages.

Find a variable string in another string using Delphi

Given a field/string like 'G,H,1,AA,T,AAA,1,E,A,H,....'. The characters can be in any combination/order.
How do I search that string and return True when searching for just 'A' or 'AA'?
i.e. If doing a search for say 'A', it should only find the 'A' between the E & H.
Regards & TIA,
Ian
You can simply split your string into an array by your delimiter and search in that array, e.g.
function FindItem(const List, Item: string): Boolean;
var
SArr: TArray<string>;
S: string;
begin
Result := False;
//Separators could also be a parameter
SArr := List.Split([',']);
for S in SArr do
begin
//use S.Trim if needed
//use AnsiSameText(S, Item) for case insensitive check
if Item = S then
Exit(True);
end;
end;
If you need to search for multiple items in your data, you might want to sort the array and use a binary search.
TArray.Sort<string>(SArr);
Result := TArray.BinarySearch(SArr, Item, Tmp);
Another approach would be using regular expressions with word boundaries to search for whole words only
Result := TRegex.IsMatch(List, '\bA\b');
Split this string into a list, for example with TStringList.CommaText (alternatively, into an array with StrUtils.SplitString()).
Then, just walk through the list and check every string (or use TStrings.IndexOf() - note: it uses CaseSensitive property, as Remy mentioned in comments).
If you are going to make many queries for the same list - sort it and use an effective binary search (TStringList.Find()).

How to get last string in TStringList

I've been searching for days on how to do this, and nothing is exactly what I need to do (or I just don't understand how to implement the solution).
What I need to do is parse a string, which is a street address, into a TStringList, and then set those strings from the list to variables I can then pass to the rest of the program. I already have the list working ok:
var
AddressList : TStringList;
i : integer;
begin
AddressList := TStringList.Create;
AddressList.Delimiter := ' ';
AddressList.DelimitedText := RawAddressStr; //RawAddressStr is parsed from another file and the string result is something like '1234 Dark Souls Lane Wyoming, MI 48419'
for i := 0 to AddressList.Count-1 do
AddressList.Strings[i]; //Not sure what to do here
The issue is that the address isn't always the same length. The address will sometimes be like '1234 Dark Souls Two Lane Wyoming...' or '1234 Dark Lane Wyoming...'
So I need to be able to use this TStringList to set the Zip, State and City into variables for later use. I would use TStringList.Find, but the ZIP code isn't always the same. Is there a way to get the last string and then go backwards from there? (Going backwards because once I get the City, State ZIP I can remove that from the RawAddressStr and then set the rest to the address string.)
Thanks.
Edit, here's the code I needed (thanks to below comments):
AddressList := TStringList.Create;
AddressList.Delimiter := ' ';
AddressList.DelimitedText := RawAddressStr;
for i := 0 to AddressList.Count-1 do
LastIndex := AddressList.Count - 1;
ZipStr := AddressList[LastIndex];
StateStr := AddressList[LastIndex - 1];
CityStr := AddressList[LastIndex - 2];
Now I can use these with StringReplace to take out the City, State Zip from the full address string, and set that as the Address string to use.
I am a little bit unsure of exactly what you're looking for since you ask
of how to get the last string of your StringList,
then at the end of the question you ask
Is there a way to get the last string and then go backwards from
there?
If you want to get the last string of a StringList you can use
var AddressList : TStringList;
MyString: string;
begin
MyString := AddressList.Last; //this...
MyString := AddressList.Strings[AddressList.Count-1]; //...and this is essentially the same thing
end;
if you would like to for-loop in reverse or backwards you should write:
for i := AddressList.Count-1 downto 0 do
AddressList.Strings[i]; //Not sure what to do here
Notice that it says "DOWNTO" and not "TO".
Now, if you would like to stop at a specific string, lets say the ZIP code,
you need to make your software understand what it is reading.
Which one of the delimited strings is the City?
Which one is the address?
To the software, a string is a string, it doesn't know, or even care what it is reading.
So I would like to suggest that you have a database of citys which it can compare the strings
of AddressList t,o and see if there is a match.
You could also implement some logic in to your algorithm.
If you know that the last string of your delimited AdressList string always is the City-name,
then you know you have the city name right there which you can use.
If you know that everything between the ZIP code and the City Name is the Street Address,
then just copy everything between the ZIP and the City-name and use that as a Street-name information.

Combine String and Character in Delphi

function leftstr(s:string; n:Integer):string;
var
i:integer;
t:string;
begin
//init var t
t := '';
for i := 0 to n do
begin
//main loop
t := t + s[i];
end;
//return results
Result:=t;
end;
So when I run this function and use ShowMessage to get the value of t in each increment of i, t is always blank. Can someone tell me the problem? This is compiled using Delphi XE6
The problem is that strings use 1-based indexing and you are accessing out of bounds. Your loop should be:
for i := 1 to n do
Probably what happens is that s[0] refers to some part of the string's meta data (length or reference count, I cannot remember which) that happens to contain a zero byte. This is then interpreted as a null terminator when the string is passed to ShowMessage.
If you had enabled the range checking feature in your compiler options, the compiler would have inserted code that would have found the error for you. This range checking feature is an astonishingly neglected and overlooked feature.
Of course, the function is a little pointless, as well as rather inefficient. You can use LeftStr from the StrUtils unit. Or plain old Copy from the System unit.

How to do this the fastest way?

I need to find out how many time one word appears in the string, but the catch is that the word you need to find can have spaces in between, for example you want to see how many times word text appears in *tOeOxOt" and it would give you output 1, or for example in textt it would give you output 2, I have written this procedure in pascal for this
procedure search(x:integer; i:integer);
var
x2:integer;
begin
x2:=x+1;
while (x2<=n) and (x2>0) do begin
if myarray[x2]=mystring[i+1] then
if i=length(mystring)-1 then
final:=final+1
else
search(x2,i+1);
x2:=x2+1;
end;
end;
and it checks number of time it appears from one letter, for example if I have ttext it would only give me one because I only check from the first t so I call the function every time I find a t in the string, but this method is too slow for 2D arrays with many characters, like 1000x1000 so I am looking for a faster solution.
You could check the array twice, on the first run trough it remove all spaces.
On the second one use a compare function like this(x is the array in which you search, y is the sub-string you are searching for and i is the current element you are checking):
function compare(var x,y:myarray; i:integer):boolean;
var l:integer;
Begin
compare:=false;
for l:=1 to length(y) do Begin
if x[i+l] <> y[l] then Exit;
End;
compare:=true;
End;
on each element of your array.

Resources