Bianca
Bianca

Reputation: 973

Removing specific lines on memo

I have memo with unwanted character in lines, and I want to remove them all. Here is my code:

var
  del: Integer;
begin
  for del := 0 to m0.Lines.Count - 1 do
  begin
    if (AnsiContainsStr(m0.Lines[del], 'remove me')) then
    begin
      m0.Lines.Delete(del);
    end;
  end;
end;

With the code above still left some lines that i wanted to remove. It only delete some of them. So I tried with another approach and this is do the job.

var
  i, r, n: Integer;
begin
  for i := 0 to m0.Lines.Count - 1 do
  begin
    if (AnsiContainsStr(m0.Lines[i], 'remove me')) then
    begin
      for r := 0 to m0.Lines.Count - 1 do
      begin
        if (AnsiContainsStr(m0.Lines[r], 'remove me')) then
        begin
          for n := 0 to m0.Lines.Count - 1 do
          begin
            if (AnsiContainsStr(m0.Lines[n], 'remove me')) then
            begin
              m0.Lines.Delete(n);
            end;
          end;
          m0.Lines.Delete(r);
        end;
      end;
      m0.Lines.Delete(i);
    end;
  end;
end;

I think this is not right, and i should not do this. How to do such job elegantly ?

Upvotes: 1

Views: 4422

Answers (3)

mg30rg
mg30rg

Reputation: 1349

This task ask for another approach. A for cycle will dynamically inrease line indexes and causes lines after the deleted ones to be skipped because their indexes decrease.


You should use a while loop instead like:

intIndex := 0; // starting at the first line

while intIndex < m0.Lines.Count do // iterating 'till the last line
begin
  if (AnsiContainsStr(m0.Lines[intIndex], 'remove me')) then // if the current line contains the text
    m0.Lines.Delete(intIndex) // delete that line and DON'T increase the index
  else
    Inc(intIndex); // increase the index
end;

Upvotes: 2

David Heffernan
David Heffernan

Reputation: 613053

When you delete a line, you change the index of all subsequent lines. Lines that you are yet to process. You also invalidate your loop because once you delete a line the upper limit on the for loop is out of bounds. Your first block of code reads beyond the end of the list.

Consider a list with 3 lines. You look at the first line, index 0, and choose to delete it. Now there are two lines left. You next need to look at lines 1 and 2 from original list, but they are now numbered 0 and 1. Your loop won't do the job. You will skip over the line that is newly indexed 0.

The standard trick is to process the list in reverse order. Then when you delete an item the lines whose indices change have already been processed. In pseudo-code:

for i := Count-1 downto 0 do
  if DeleteThisItem(i) then
    Delete(i);

The key point is that whenever you use index i then you are referring to the item that had index i before the loop started.

Upvotes: 3

NGLN
NGLN

Reputation: 43659

Because your loop runs from 0 to Count - 1, the line after a deleted line will be skipped.

Explanation: suppose line 3 needs to get deleted. You delete it, and now line 4 will be line 3. The loop variable i will be incremented to 4 on the next run, thus the new line 3 is never evaluated.

Solution: run your loop in reverse:

for i := m0.Lines.Count - 1 downto 0 do

Upvotes: 9

Related Questions