We help IT Professionals succeed at work.

Ignore duplicates in a listview.

Peter Kiers
Peter Kiers used Ask the Experts™
on
Hi,

I have a Button, a ListView and a XMLDocument-component on my form.
When I press on the button it uses the XMLDocument-component to parse
the xml-file hat is specified in the procedure. It searches for the nodes that
begins with 'BG' and displays from every node that begins with BG the values
from Val, Dt and TM into the Listview columns Value, Date and Time.

But everytime when I presses the button the xm-file will be read
and the listview will be fill with records that are allready exits.
Is there a way to ignore dulpicate rows?

I found this on the internet: FRecentFiles.Duplicates := dupIgnore;

But don't know exactly what to do with it?

Who knows the answer and is willing to help me?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2010

Commented:
insert ListView1.Items.Clear in the begin of initialization proc:
procedure TForm1.Button1Click(Sender: TObject);
const
  FormatFrom: TFormatSettings = (DateSeparator: '-'; ShortDateFormat: 'yyyy-mm-dd');
  FormatTo: TFormatSettings = (DateSeparator: '-'; ShortDateFormat: 'dd-mm-yyyy');
  DoubleFormat: TFormatSettings = (DecimalSeparator: '.');
  Days : array[1..7] of string
    = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
var
  LoopNodes : IDOMNodeList;
  i: Integer;
  DateTime: TDateTime;
  Day: string;
  Val: Extended;
  strVal, strValText: string;
begin
  XMLDocument1.FileName := '.\XMLDocument1.xml';
  XMLDocument1.Active := True;
  try
    LoopNodes:= XMLDocument1.DOMDocument.getElementsByTagName( 'BG' );

    ListView1.Items.BeginUpdate;
    try
      ListView1.Items.Clear;
      for i:= 0 to LoopNodes.length -1 do
        with ListView1.Items.add do
        begin
          if TryStrToDate(LoopNodes[i].attributes.getNamedItem('Dt').nodeValue, DateTime, FormatFrom) then
          begin
            Caption:= Days[ DayOfWeek(DateTime) ];
            SubItems.Add( DateToStr( DateTime, FormatTo ) );
          end
          else
          begin
            Caption:= 'can''t convert date';
            SubItems.Add( LoopNodes[i].attributes.getNamedItem('Dt').nodeValue );
          end;

          SubItems.Add( LoopNodes[i].attributes.getNamedItem('Tm').nodeValue );
          SubItems.Add( LoopNodes[i].attributes.getNamedItem('Val').nodeValue );

          strVal:= StringReplace(LoopNodes[i].attributes.getNamedItem('Val').NodeValue, ',', '.', []);
          strValText:= '';
          if TryStrToFloat( strVal, Val, DoubleFormat ) then
            if Val < 4 then
              strValText:= 'HYPO'
            else if Val > 10 then
              strValText:= 'HYPER';
          SubItems.Add( strValText );

        end;
    finally
      ListView1.Items.EndUpdate;
    end;

  finally
    XMLDocument1.Active := False;
  end;
end;

Open in new window

Peter KiersOperator

Author

Commented:
When i click on the button data will be parsed from the xml-file and put in the listview.
When more data on the xml-file will be added, and i click on the button again. Only
the records that are not allready in the listview have to be added to the listview.

P.
MerijnBSr. Software Engineer

Commented:
What makes a entry in the listview duplicate? Which data? Is it only duplicate if all columns are equal or is there a column which is always unique?
Top Expert 2010

Commented:
Do you make any changes with data when they are always loaded into ListView?

If no, then you can just clear ListView and load all data again.
Peter KiersOperator

Author

Commented:
I have put a piece of the xml-file in the code-section that will be parsed and put in
the listview. Every time I press on the button it will parse the data of the xml-file.
But the data of the xml-file changes too. So when I press the button again I get
dulpicate record in the listview.
<BG Val="7.1" Dt="2010-05-30" Tm="11:58" D="1"/>
<BG Val="9.2" Dt="2010-05-30" Tm="10:40" D="1"/>
<BG Val="4.5" Dt="2010-05-29" Tm="23:14" D="1"/>
<BG Val="4.9" Dt="2010-05-29" Tm="20:21" D="1"/>
<BG Val="18.0" Dt="2010-05-29" Tm="18:55" D="1"/>
<BG Val="28.0" Dt="2010-05-29" Tm="17:08" D="1"/>
<BG Val="12.5" Dt="2010-05-29" Tm="12:39" D="1"/>
<BG Val="12.5" Dt="2010-05-29" Tm="10:28" D="1"/>
<BG Val="6.8" Dt="2010-05-28" Tm="23:42" D="1"/>
<BG Val="7.5" Dt="2010-05-28" Tm="17:43" D="1"/>
<BG Val="16.9" Dt="2010-05-28" Tm="12:04" D="1"/>
<BG Val="8.5" Dt="2010-05-28" Tm="08:13" D="1"/>
<BG Val="4.2" Dt="2010-05-27" Tm="23:14" D="1"/>
<BG Val="14.3" Dt="2010-05-27" Tm="21:18" D="1"/>
<BG Val="8.2" Dt="2010-05-27" Tm="17:45" D="1"/>
<BG Val="7.3" Dt="2010-05-27" Tm="17:09" D="1"/>
<BG Val="10.2" Dt="2010-05-27" Tm="13:48" D="1"/>
<BG Val="13.1" Dt="2010-05-27" Tm="12:45" D="1"/>
<BG Val="13.4" Dt="2010-05-27" Tm="12:01" D="1"/>
<BG Val="6.1" Dt="2010-05-27" Tm="08:07" D="1"/>
<BG Val="6.9" Dt="2010-05-26" Tm="23:45" D="1"/>
<BG Val="15.9" Dt="2010-05-26" Tm="21:30" D="1"/>
<BG Val="6.1" Dt="2010-05-26" Tm="16:27" D="1"/>
<BG Val="8.5" Dt="2010-05-26" Tm="10:44" D="1"/>
<BG Val="5.8" Dt="2010-05-26" Tm="07:53" D="1"/>
<BG Val="4.2" Dt="2010-05-25" Tm="22:58" D="1"/>
<BG Val="10.1" Dt="2010-05-25" Tm="22:05" D="1"/>
<BG Val="14.1" Dt="2010-05-25" Tm="21:28" D="1"/>
<BG Val="20.4" Dt="2010-05-25" Tm="20:31" D="1"/>
<BG Val="19.5" Dt="2010-05-25" Tm="18:52" D="1"/>
<BG Val="14.8" Dt="2010-05-25" Tm="17:52" D="1"/>
<BG Val="10.8" Dt="2010-05-25" Tm="17:22" D="1"/>
<BG Val="4.2" Dt="2010-05-25" Tm="16:01" D="1"/>
<BG Val="16.4" Dt="2010-05-25" Tm="12:08" D="1"/>
<BG Val="13.0" Dt="2010-05-25" Tm="10:55" D="1"/>
<BG Val="6.7" Dt="2010-05-25" Tm="08:04" D="1"/>
<BG Val="9.5" Dt="2010-05-25" Tm="00:00" D="1"/>
<BG Val="3.6" Dt="2010-05-24" Tm="23:14" D="1"/>
<BG Val="7.3" Dt="2010-05-24" Tm="22:14" D="1"/>
<BG Val="19.8" Dt="2010-05-24" Tm="20:59" D="1"/>
<BG Val="13.5" Dt="2010-05-24" Tm="17:27" D="1"/>

Open in new window

Peter KiersOperator

Author

Commented:
When the data is loaded in the listview the will be automaticly save to file.
Top Expert 2010

Commented:
did you tried my attached code http://#33113116

why don't it suit you?
Peter KiersOperator

Author

Commented:
I get unvalid adsress
Top Expert 2010

Commented:
Peter KiersOperator

Author

Commented:
Because I deletes everything in the listview.
f.e: when I have 1000 records in the listview
and i  use the onclick-proc with the clear sentence
it makes the listview empty and load new data
from the xml-file.

While it have to be like this:
When there is 1000 records in the listview.
And i press the button it have to load only
those records from the xml-file that are not
already in the listview.

P.
Top Expert 2010

Commented:
>> to load only those records from the xml-file that are not already in the listview.

are these records equal?

<BG Val="8.2" Dt="2010-05-27" Tm="17:45" D="1"/>
<BG Val="7.3" Dt="2010-05-27" Tm="17:45" D="1"/>

in other words would you like to compare records only by date and time or by val and d too?
Peter KiersOperator

Author

Commented:
I geuss Date and Time would be enough to prevent duplicate records.
Peter KiersOperator

Author

Commented:
Oh, and val too.
MerijnBSr. Software Engineer

Commented:
Is there a moment in your app when this has to be reset?
How are you going to delete these duplicate records in the end?
Peter KiersOperator

Author

Commented:
I'll be back tonight...

PK
Top Expert 2010

Commented:
ok. try this code.


function Node_GetHash(ANode: IDOMNode): Cardinal;
const
  NodeTm_FormatFrom: TFormatSettings = (TimeSeparator: ':'; ShortTimeFormat: 'hh:mm');
var
  Tm: TDateTime;
begin
  Result:= 0;
  if TryStrToTime( ANode.attributes.getNamedItem('Tm').nodeValue, Tm, NodeTm_FormatFrom ) then
    Result:= Cardinal(Round(Tm*10000)); 
end;

function NodeDt_Convert(const Dt: string; var ADate: TDateTime): string;
const
  NodeDt_FormatFrom: TFormatSettings = (DateSeparator: '-'; ShortDateFormat: 'yyyy-mm-dd');
  NodeDt_FormatTo: TFormatSettings = (DateSeparator: '-'; ShortDateFormat: 'dd-mm-yyyy');
begin
  if TryStrToDate(Dt, ADate, NodeDt_FormatTo) then
    Result:= DateToStr( ADate, NodeDt_FormatTo )
  else
  begin
    ADate:= 0;
    Result:= 'can''t convert date';
  end;
end;

procedure ListView_Add(AListView: TListView; ANode: IDOMNode);
const
  DoubleFormat: TFormatSettings = (DecimalSeparator: '.');
  Days : array[1..7] of string
    = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
var
  DateTime: TDateTime;
  strDate: string;
  Val: Extended;
  strVal, strValText: string;
begin
  with AListView.Items.Add do
  begin
    strDate:= NodeDt_Convert( ANode.attributes.getNamedItem('Dt').nodeValue, DateTime );
    if DateTime <> 0 then
      Caption:= Days[ DayOfWeek(DateTime) ]
    else
      Caption:= 'can''t convert date';

    Data:= Pointer(Node_GetHash( ANode ));

    SubItems.Add( strDate );
    SubItems.Add( ANode.attributes.getNamedItem('Tm').nodeValue );
    SubItems.Add( ANode.attributes.getNamedItem('Val').nodeValue );

    strVal:= StringReplace(ANode.attributes.getNamedItem('Val').NodeValue, ',', '.', []);
    strValText:= '';
    if TryStrToFloat( strVal, Val, DoubleFormat ) then
      if Val < 4 then
        strValText:= 'HYPO'
      else if Val > 10 then
        strValText:= 'HYPER';
    SubItems.Add( strValText );
  end;
end;

function ListViewItem_IsEqual( AListItem: TListItem; ANode: IDOMNode ): Boolean;
var
  Dt: TDateTime;
begin
  Result:= (AListItem.SubItems[0] = NodeDt_Convert( ANode.attributes.getNamedItem('Dt').nodeValue, Dt )) and
           (AListItem.SubItems[1] = ANode.attributes.getNamedItem('Tm').nodeValue) and
           (AListItem.SubItems[2] = ANode.attributes.getNamedItem('Val').nodeValue);
end;

function ListView_Find(AListView: TListView; ANode: IDOMNode): TListItem;
var
  CurIndex: Integer;
begin
  CurIndex:= 0;
  repeat
    Result:= AListView.FindData( CurIndex, Pointer(Node_GetHash(ANode)), True, False);
    if Assigned(Result) then
    begin
      CurIndex:= Result.Index + 1;
      if ListViewItem_IsEqual( Result, ANode ) then
        Exit;
    end;
  until Result=nil;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  LoopNodes : IDOMNodeList;
  i: Integer;
begin
  XMLDocument1.FileName := '.\XMLDocument1.xml';
  XMLDocument1.Active := True;
  try
    LoopNodes:= XMLDocument1.DOMDocument.getElementsByTagName( 'BG' );

    ListView1.Items.BeginUpdate;
    try
      for i:= 0 to LoopNodes.length -1 do

        if ListView_Find(ListView1, LoopNodes[i]) = nil then
          ListView_Add(ListView1, LoopNodes[i]);

    finally
      ListView1.Items.EndUpdate;
    end;

  finally
    XMLDocument1.Active := False;
  end;
end;

Open in new window

Top Expert 2010
Commented:
typo in NodeDt_Convert function. Here is fixed code:


function Node_GetHash(ANode: IDOMNode): Cardinal;
const
  NodeTm_FormatFrom: TFormatSettings = (TimeSeparator: ':'; ShortTimeFormat: 'hh:mm');
var
  Tm: TDateTime;
begin
  Result:= 0;
  if TryStrToTime( ANode.attributes.getNamedItem('Tm').nodeValue, Tm, NodeTm_FormatFrom ) then
    Result:= Cardinal(Round(Tm*10000)); 
end;

function NodeDt_Convert(const Dt: string; var ADate: TDateTime): string;
const
  NodeDt_FormatFrom: TFormatSettings = (DateSeparator: '-'; ShortDateFormat: 'yyyy-mm-dd');
  NodeDt_FormatTo: TFormatSettings = (DateSeparator: '-'; ShortDateFormat: 'dd-mm-yyyy');
begin
  if TryStrToDate(Dt, ADate, NodeDt_FormatFrom) then
    Result:= DateToStr( ADate, NodeDt_FormatTo )
  else
  begin
    ADate:= 0;
    Result:= 'can''t convert date';
  end;
end;

procedure ListView_Add(AListView: TListView; ANode: IDOMNode);
const
  DoubleFormat: TFormatSettings = (DecimalSeparator: '.');
  Days : array[1..7] of string
    = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
var
  DateTime: TDateTime;
  strDate: string;
  Val: Extended;
  strVal, strValText: string;
begin
  with AListView.Items.Add do
  begin
    strDate:= NodeDt_Convert( ANode.attributes.getNamedItem('Dt').nodeValue, DateTime );
    if DateTime <> 0 then
      Caption:= Days[ DayOfWeek(DateTime) ]
    else
      Caption:= 'can''t convert date';

    Data:= Pointer(Node_GetHash( ANode ));

    SubItems.Add( strDate );
    SubItems.Add( ANode.attributes.getNamedItem('Tm').nodeValue );
    SubItems.Add( ANode.attributes.getNamedItem('Val').nodeValue );

    strVal:= StringReplace(ANode.attributes.getNamedItem('Val').NodeValue, ',', '.', []);
    strValText:= '';
    if TryStrToFloat( strVal, Val, DoubleFormat ) then
      if Val < 4 then
        strValText:= 'HYPO'
      else if Val > 10 then
        strValText:= 'HYPER';
    SubItems.Add( strValText );
  end;
end;

function ListViewItem_IsEqual( AListItem: TListItem; ANode: IDOMNode ): Boolean;
var
  Dt: TDateTime;
begin
  Result:= (AListItem.SubItems[0] = NodeDt_Convert( ANode.attributes.getNamedItem('Dt').nodeValue, Dt )) and
           (AListItem.SubItems[1] = ANode.attributes.getNamedItem('Tm').nodeValue) and
           (AListItem.SubItems[2] = ANode.attributes.getNamedItem('Val').nodeValue);
end;

function ListView_Find(AListView: TListView; ANode: IDOMNode): TListItem;
var
  CurIndex: Integer;
begin
  CurIndex:= 0;
  repeat
    Result:= AListView.FindData( CurIndex, Pointer(Node_GetHash(ANode)), True, False);
    if Assigned(Result) then
    begin
      CurIndex:= Result.Index + 1;
      if ListViewItem_IsEqual( Result, ANode ) then
        Exit;
    end;
  until Result=nil;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  LoopNodes : IDOMNodeList;
  i: Integer;
begin
  XMLDocument1.FileName := '.\XMLDocument1.xml';
  XMLDocument1.Active := True;
  try
    LoopNodes:= XMLDocument1.DOMDocument.getElementsByTagName( 'BG' );

    ListView1.Items.BeginUpdate;
    try
      for i:= 0 to LoopNodes.length -1 do

        if ListView_Find(ListView1, LoopNodes[i]) = nil then
          ListView_Add(ListView1, LoopNodes[i]);

    finally
      ListView1.Items.EndUpdate;
    end;

  finally
    XMLDocument1.Active := False;
  end;

end;

Open in new window

Peter KiersOperator

Author

Commented:
It works perfect...