Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
Solved

# How do I find duplicate entries in a TSringlist

Posted on 2002-05-12
Medium Priority
274 Views
I would like to be able to search through a stringlist for duplicate entries and add an associated number, for example.

item number    quantity
1                  5
2                  4
3                  2
1                  3

How do I search through this list for each enrty of item number 1. When item number 1 is found I wish to add each quantity to get the total quantity. The resultant list will be.

item number    quantity
1                  8
2                  4
3                  2

Ivan
0
Question by:icarey
[X]
###### Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

• Help others & share knowledge
• Earn cash & points
• 3
• 2
• 2
• +3

LVL 1

Expert Comment

ID: 7005235
I've used tstringlist.objects to store quantity of equal items.

It isn't clear from question:
1) Is quantity defined initially or not?
2) Is it necessary add quantity number in strings?

procedure TForm1.Button1Click(Sender: TObject);
var i,j:integer;
sl:tstringlist;
begin
randomize;
sl:=tstringlist.create;
for i:=1 to 20 do
i:=0;
while i<sl.count do begin
j:=sl.IndexOf(sl[i]);
if j=i then begin
sl.Objects[i]:=pointer(1);
inc(i);
end
else begin
sl.Objects[j]:=pointer(Integer(sl.Objects[j])+1);
sl.Delete(i);
end;
end;
sl.Sorted:=true;//if you need
for i:=0 to sl.Count-1 do
sl.free;
end;
0

Expert Comment

ID: 7005318
Hey there!...

Load the Tstrings into a listbox1 and then call this procedure.  Change the properties of listbox1 to sorted.  This will cause the values to group together.

I'm guessing Item Numbers and Quantity Numbers were in the same string and separated by a ';' sign.

Goodluck...

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
ListBox2: TListBox;
ListBox3: TListBox;
ListBox4: TListBox;
ListBox5: TListBox;
ListBox6: TListBox;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{\$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
count1, m, num1, j, num2, temp1 : integer;
itemnumber1, quantity1, itemnumber2, quantity2, word1, word2, wordline1, wordline2 : string;
begin
num1 := 0;
num2 := 0;
count1 := 0;
word1 := '';
word2 := '';
WHILE count1 <= (listbox1.Items.count-2) DO BEGIN
//Wordline stores the active line as a string.
wordline1 := listbox1.Items[count1];
wordline2 := listbox1.Items[(count1 + 1)];
FOR m := 1 TO length(wordline1) DO BEGIN
//Search and extract item-number and quantity.
IF wordline1[m] = ';' THEN BEGIN
IF num1 = 0 THEN BEGIN
itemnumber1 := word1;
num1 := num1 + 1;
word1 := '';
END
ELSE IF num1 = 1 THEN BEGIN
quantity1 := word1;
num1 := 0;
word1 := '';
END;
END
ELSE BEGIN
word1 := word1 + wordline1[m];
END;
END;
//Check next line.
FOR j := 1 TO length(wordline2) DO BEGIN
//Search and extract item-number and quantity.
IF wordline2[j] = ';' THEN BEGIN
IF num2 = 0 THEN BEGIN
itemnumber2 := word2;
num2 := num2 + 1;
word2 := '';
END
ELSE IF num2 = 1 THEN BEGIN
quantity2 := word2;
num2 := 0;
word2 := '';
END;
END
ELSE BEGIN
word2 := word2 + wordline2[j];
END;
END;
//Fill listbox1.
IF itemnumber1 = itemnumber2 THEN BEGIN
temp1 := strtoint(quantity1) + strtoint(quantity2);
quantity1 := inttostr(temp1);
listbox1.items[count1] := itemnumber1 + ';' + quantity1 + ';';
listbox1.Items.delete(count1 + 1);
count1 := count1 - 1;
END
ELSE IF itemnumber1 <> itemnumber2 THEN BEGIN
listbox1.items[count1] := itemnumber1 + ';' + quantity1 + ';';
END;
//Move to next line.
count1 := count1 + 1;
END;
end;

end.
0

Expert Comment

ID: 7005415
Sorry icarey,

I forgot to metion add a ';' at the end also.  Input should look like this...

1;5;
2;4;
3;2;
1;3;

0

LVL 4

Expert Comment

ID: 7006793
HI, icarey,
Kind of the following:

SL:=TStringList.Create;
for i:=0 to SLwithDupl.Count-1 do
begin
j:=SL.Index(SLwithDupl[i]);
if j=-1
else SL[j].quantity:=SL[j].quantity+SLwithDupl[i].quantity;
end;
That's all. It's not the code, of course, cause I don't know how you create and insert the quantity (what kind of object
you use for that). You must convert what I said in the
SL.Objects[..] and so on.
Sincerely,
Nestorua.
0

LVL 2

Expert Comment

ID: 7007441
Hi,

I do a lot of this kind of work so I have a ready-made class which I use for this kind of work.

- you don't need to worry about finding the item in the list because that's automatic
- it's easy to get the info out

You use it something like this:

with TTotalingStringList.Create do
try

ShowMessage(inttostr(TotalForItem('dilbert')));
finally
Free;
end;

MP

unit uTotalingStringList;

interface

uses
Classes,

uHandy;

type
TTotalingStringList=class(TStringList)
public
constructor Create;
function TotalForItem(sItem:string):LongInt;
end;

implementation

constructor TTotalingStringList.Create;
begin
inherited;

Duplicates:=dupError;
end;

var
iFound:integer;
begin
iFound:=IndexOf(sItem);
if iFound>=0 then
begin
Result:=LongInt(Objects[iFound])+iAmt;
Objects[iFound]:=Pointer(Result);
end
else
begin
Result:=iAmt;
end;
end;

function TTotalingStringList.TotalForItem(sItem:string):LongInt;
var
iFound:integer;
begin
iFound:=IndexOf(sItem);
if iFound>=0 then
Result:=LongInt(Objects[iFound])
else
Result:=0;
end;

end.
0

LVL 3

Author Comment

ID: 7007844
MBo
Let me explain a little better.

I have a comma delimited text file which I load in to a TSrtingList.
In it I have say:
'Hammer,5'
'Wrench,4'
'Screwdriver,2'
'Hammer,3'

I would now like to search through this list for each enrty of an item. For example in the list above I would like to be able to produce a list with no duplicate entries for Hammer and add the total quantity numbers.
The resulting list that I will save to a new comma delimited text file will be:

'Hammer,8'
'Wrench,4'
'Screwdriver,2'

Note: now we only have one entry for Hammer with a total quantity of 8

I hope this explains better.

Thanks to all
Ivan
0

LVL 1

Accepted Solution

MBo earned 484 total points
ID: 7008147
OK. Corrected variant.

procedure TForm1.Button1Click(Sender: TObject);
var i,j:integer;
sl:tstringlist;

function SameName(const I1,I2:Integer):boolean;
begin
//this function from sysutils ignores case
//if case sensitivity is necessary, use next commented lines
//Result:=Copy(sl[i1],1,pos(',',sl[i1])-1)=
//  Copy(sl[i2],1,pos(',',sl[i2])-1);

Result:=SameText(Copy(sl[i1],1,pos(',',sl[i1])-1),
Copy(sl[i2],1,pos(',',sl[i2])-1));
end;

procedure Join(const Src,Dst:Integer);
var i,i1,i2:integer;
begin
i:=pos(',',sl[Dst]);
i1:=StrToInt(Copy(sl[Dst],i+1,Length(sl[Dst])-i));
i2:=StrToInt(Copy(sl[Src],i+1,Length(sl[Src])-i));
sl[Dst]:=Copy(sl[Dst],1,i)+IntToStr(i1+i2);
sl.Delete(Src);
end;

begin
sl:=tstringlist.create;
stemp:=tstringlist.create;
i:=0;
while i<sl.count do begin
j:=0;
while j<i do begin
if SameName(i,j) then begin
Join(i,j);
Dec(i);
Break;
end;
inc(j);
end;
inc(i);
end;
sl.savetofile('e:\ww1.txt');
sl.free;
end;

In some spaces occur in input file ( Hammer,  5)
use Trim in front of Copy or I can write example using CommaText (slower for long files)

0

LVL 1

Expert Comment

ID: 7008262
Hi icarey,

Load the text file into a list box
and copy this code button click...

You can get the result!

bye
SBSen.

procedure TForm1.Button1Click(Sender: TObject);
var
St : TStringList;
Idx,Del : Integer;
Nm,Val : String;
begin
St := TStringList.Create;
for Idx := 0 to ListBox1.Items.Count-1 do
begin
Del := LastDelimiter(',',ListBox1.Items[Idx]);
if Del > 0 then
begin
Nm := Copy(ListBox1.Items[Idx],0,LastDelimiter(',',ListBox1.Items[Idx])-1);
Val := Copy(ListBox1.Items[Idx],LastDelimiter(',',ListBox1.Items[Idx])+1,MaxInt);
if st.Values[Nm] = EmptyStr then
else
try
st.Values[Nm] := IntToStr(StrToInt(st.Values[Nm])+ StrToInt(Val));
except
ShowMessage('Econversion Error');
end;
end;
end;
ShowMessage(St.Text);
end;
0

LVL 1

Expert Comment

ID: 7008352
hi,
Correction!!
'Hammer,5' if have upper quotes "'" in each string then there will be a problem we have check
Val := Copy(ListBox1.Items[Idx],LastDelimiter(',',ListBox1.Items[Idx])+1,MaxInt);
Val[Pos(',',Val)] := ' ';

one more sugession instead of , u can use =
ie Hammer=5
u can use Names and values of TStrings
or manupulate with TInifile

bye
SBSen.
0

Expert Comment

ID: 7010258
icarey?...
0

LVL 3

Author Comment

ID: 7013074
Thanks to all for helping
0

## Featured Post

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi databaseâ€¦
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGridâ€¦
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized â€¦
In this video, Percona Director of Solution Engineering Jon Tobin discusses the function and features of Percona Server for MongoDB. How Percona can help Percona can help you determine if Percona Server for MongoDB is the right solution for â€¦
###### Suggested Courses
Course of the Month9 days, 17 hours left to enroll