-2

I have two StringList that are loaded (from a file) with users and users + password respectivally. I'm comparing these lists to determine what user (of first list) already have a password (in second list) and then insert on ListView who have and also who still not have.

But exists a problem here that from second ListItem.Caption (user) is repeting two times.

enter image description here

How i can solve this?

My files that are loaded on lists are:

users.dat

  • User01
  • User02
  • User03

logins.dat

  • User01|test01
  • User01|test01

And this was my last attempt of code:

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListView1: TListView;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  L1, L2, LSplit: TStringList;
  L: TListItem;
  I, J: Integer;
begin
  L1 := TStringList.Create;
  L2 := TStringList.Create;
  LSplit := TStringList.Create;

  L1.LoadFromFile('users.dat');
  L2.LoadFromFile('logins.dat');

  for I := 0 to L1.Count - 1 do
  begin
    for J := 0 to L2.Count - 1 do
    begin
      LSplit.Clear;
      ExtractStrings(['|'], [], PChar(L2[J]), LSplit);
      if L1[I] = LSplit[0] then
      begin
        L := ListView1.Items.Add;
        L.Caption := LSplit[0];
        L.SubItems.Add(LSplit[1]);
        Break;
      end;
      L := ListView1.Items.Add;
      L.Caption := L1[I];
    end;
  end;
  L1.Free;
  L2.Free;
  LSplit.Free;
end;

end.
BrowJr
  • 415
  • 5
  • 20
  • 2
    If you use your debugger and step through your program as it executes you will see why it behaves as it does. Debugging is by far the most efficient way to understand issues like this. I strongly recommend some time doing so. Even now that you have answers to this question, you will still learn vital skills by debugging the erroneous code and so improving your debugging techniques. These skills can be reused and further honed the next time you encounter behaviour that is not as expected. – David Heffernan Aug 29 '19 at 07:01

3 Answers3

2

Your inner loop is broken. It is adding items to the ListView even when the 2 StringList items being compared don't match each other. For each user in the first list, you are adding it to the ListView as many times as there are entries in the second list.

Your code should look more like this instead:

procedure TForm1.Button1Click(Sender: TObject);
var
  L1, L2, LSplit: TStringList;
  L: TListItem;
  I, J: Integer;
begin
  L1 := TStringList.Create;
  try
    L2 := TStringList.Create;
    try
      LSplit := TStringList.Create;
      try
        L1.LoadFromFile('users.dat');
        L2.LoadFromFile('logins.dat');

        for I := 0 to L1.Count - 1 do
        begin
          L := ListView1.Items.Add;
          L.Caption := L1[I];
          for J := 0 to L2.Count - 1 do
          begin
            LSplit.Clear;
            ExtractStrings(['|'], [], PChar(L2[J]), LSplit);
            if L1[I] = LSplit[0] then
            begin
              L.SubItems.Add(LSplit[1]);
              Break;
            end;
          end;
        end;
      finally
        LSplit.Free;
      end;
    finally
      L2.Free;
    end;
  finally
    L1.Free;
  end;
end;

That being said, you don't need 3 TStringList objects and 2 loops. 2 TStringList objects and 1 loop will suffice:

procedure TForm1.Button1Click(Sender: TObject);
var
  L1, L2: TStringList;
  L: TListItem;
  I: Integer;
begin
  L1 := TStringList.Create;
  try
    L2 := TStringList.Create;
    try
      L1.LoadFromFile('users.dat');
      L2.LoadFromFile('logins.dat');
      L2.NameValueSeparator := '|';

      for I := 0 to L1.Count - 1 do
      begin
        L := ListView1.Items.Add;
        L.Caption := L1[I];
        L.SubItems.Add(L2.Values[L1[I]]);
      end;
    finally
      L2.Free;
    end;
  finally
    L1.Free;
  end;
end;
Remy Lebeau
  • 454,445
  • 28
  • 366
  • 620
1
for I := 0 to L1.Count - 1 do
  begin
    found := false;
    L := ListView1.Items.Add;
    for J := 0 to L2.Count - 1 do
    begin
      LSplit.Clear;
      ExtractStrings(['|'], [], PChar(L2[J]), LSplit);
      if L1[I] = LSplit[0] then
      begin
        L.Caption := LSplit[0];
        L.SubItems.Add(LSplit[1]);
        found := true;
        Break;
      end;
    end;
    if not found then 
       L.Caption := L1[I];
  end;

Also note that dictionary approach is much faster for large lists

MBo
  • 66,413
  • 3
  • 45
  • 68
0

The problem with your code is that you are always adding the username from your first list to the result regardles of whether you aready added it from the second list with included password or not.

To avoid this you need to write your code in a way that adding username from first list happen only if it hasn't been added from the second one.

//If password exist in second list add username and password from second list
if L1[I] = LSplit[0] then
begin
  L := ListView1.Items.Add;
  L.Caption := LSplit[0];
  L.SubItems.Add(LSplit[1]);
  Break;
end
//Else add username from first list
else
begin
  L := ListView1.Items.Add;
  L.Caption := L1[I];
end;

Also note that your approqach Will fail in case if your second list contains username that is not present in the first list.

For instance let us check next scenario: users.dat

  • User01
  • User02
  • User03

logins.dat

  • User01|test01
  • User02|test01
  • User04|test04

In the above scenario your final result won't include any data from User04 becouse it doesn't exist in your first list.

So I would recomend you to use different approach where you iterate the second list and for each entry search the first list to see if the username exists in it.
If it does you edit that entry on the first list to add the pasword infromation from the second list. And in case if you can't find username on the first list then you add both username and password to it.

This will guarantee you that final result will unclude all usernames from both lists no matter on which they were found.

SilverWarior
  • 5,651
  • 2
  • 13
  • 19