Lazarus

Programming => General => Topic started by: maurobio on July 01, 2020, 11:09:57 pm

Title: [SOLVED] Finding contiguous items selected in a checklistbox
Post by: maurobio on July 01, 2020, 11:09:57 pm
Dear ALL,

I have a dialog with a CheckListBox and want to build a string with the numbers of the items selected by the user, for example:

[X] Item 1
[ ] Item 2
[X] Item 3
[ ] Item 4
[ ] Item 5

This is obviously quite simple to achive with some code as follows:

 
Code: Pascal  [Select][+][-]
  1.  for J := 0 to CheckListBox1.Count - 1 do
  2.     if CheckListBox1.Checked[J] then
  3.       ResultStr := IntToStr(J + 1) + ' ';
  4.  
 
and the result string will be: '1 3';

However, if the user select a sequence of contiguous items, I want that the result string be returned as a 'range' giving only the first and last numbers in the sequence. For example, suppose the user selects the first three items in the sequence:

[X] Item 1
[X] Item 2
[X] Item 3
[ ] Item 4
[ ] Item 5

To build the range, I store the numbers of the selected items in a StringList and modify the above code accordingly:

Code: Pascal  [Select][+][-]
  1. S := TStringList.Create;
  2.   for J := 0 to CheckListBox1.Count - 1 do
  3.   begin
  4.     if CheckListBox1.Checked[J] then
  5.       S.Add(IntToStr(J + 1));
  6.   end;
  7.   if S.Count = 1 then
  8.      ResultStr := S[0]
  9.   else if S.Count > 1 then
  10.      ResultStr := S[0] + '-' + S[S.Count - 1]
  11.   else
  12.     ResultStr := '';
  13.   S.Free;
  14.  
In this case, the result string will be: '1-3';

So far, so good. But my problem (yes, I finally got to it!) is how to build the result string when the user selects non-contiguous items in the CheckListBox, for example:

[X] Item 1
[X] Item 2
[X] Item 3
[ ] Item 4
[X] Item 5

The result string should be a range ('1-3') plus a single number string ('5'), as follows: '1-3 5'.

My problem is therefore that of identifying if the items the user has selected are contiguous or non-contiguous. Any ideas on how to achieve this?

I attach may test source code.

Thanks in advance for any assistance you can provide.

With best wishes,
Title: Re: Finding contiguous items selected in a checklistbox
Post by: jamie on July 02, 2020, 12:01:12 am
if the current one is unchecked in the loop then examine what you have so far in the string.. If the string is empty then you just keep  going of course because you haven't yet got the first one..
 
 You need to insert a comma after an unchecked.. also at the same time you need to test of course the previous build to make sure you have closed the range...
 
 I guess I can come up with an example of a parser... BB-Soon.
Title: Re: Finding contiguous items selected in a checklistbox
Post by: Handoko on July 02, 2020, 12:19:59 am
Here is my solution:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   Selected:          string;
  4.   Limit:             Integer;
  5.   Start:             Integer;
  6.   isRange:           Boolean;
  7.   Now, Next1, Next2: Boolean;
  8.   i:                 Integer;
  9. begin
  10.   Selected := '';
  11.   Start    := 0;
  12.   Limit    := CheckListBox1.Count - 1;
  13.   isRange  := False;
  14.   for i := 0 to Limit do
  15.   begin
  16.  
  17.     // Set the values for Now, Next, Next1
  18.     Now := CheckListBox1.Checked[i];
  19.     if i < Limit then
  20.        Next1 := CheckListBox1.Checked[i+1]
  21.     else
  22.       Next1 := False;
  23.     if i < (Limit-1) then
  24.        Next2 := CheckListBox1.Checked[i+2]
  25.     else
  26.       Next2 := False;
  27.  
  28.     // Skip if Now is not checked
  29.     if not(Now) then
  30.     begin
  31.       if isRange then // Store the ranged data before continue
  32.       begin
  33.         if not(Selected.IsEmpty) then Selected := Selected + ',';
  34.         Selected := Selected + Start.ToString + '-' + (i-1).ToString;
  35.         isRange  := False;
  36.       end;
  37.       Continue;
  38.     end;
  39.  
  40.     // Continue previous range?
  41.     if isRange then Continue;
  42.  
  43.     // Start a new range?
  44.     if Next1 and Next2 then
  45.     begin
  46.       if not(isRange) then Start := i;
  47.       isRange := True;
  48.       Continue;
  49.     end;
  50.  
  51.     // Only single checked item
  52.     if not(Selected.IsEmpty) then Selected := Selected + ',';
  53.     Selected := Selected + i.ToString;
  54.  
  55.   end;
  56.  
  57.   // Close previous ranged items
  58.   if isRange then
  59.   begin
  60.     if not(Selected.IsEmpty) then Selected := Selected + ',';
  61.     Selected := Selected + Start.ToString + '-' + Limit.ToString;
  62.   end;
  63.  
  64.   ShowMessage(Selected);
  65. end;

Note:
The result is zero-based index.

Edit:
Because of line #41, the line #46 no need to test the range, so it should be only:
Start := i;


The basic logic of the code:
- If only 2 adjacent items, then they are not ranged items
- If 3 or more adjacent items, then they are ranged items
- For the reasons above, it needs to check the status of Now, Next1, Next2
Title: Re: Finding contiguous items selected in a checklistbox
Post by: maurobio on July 02, 2020, 12:47:08 am
Dears @jamie and @Handoko,

Thank you very much for your insightful answers. Handoko's code works fine!

With best wishes,
Title: Re: Finding contiguous items selected in a checklistbox
Post by: jamie on July 02, 2020, 12:58:19 am
well here's mine just in case...
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   S:String;
  4.   B:Boolean;
  5.   I,LastIndex,FirstIndex:Integer;
  6. begin
  7.   LastIndex := -1;
  8.   FirstIndex := -1;
  9.   For I := 0 To  ChecklistBox1.Items.Count-1 do With CheckListBox1 do
  10.    begin
  11.      if Checked[i] Then
  12.       Begin
  13.        if firstIndex = -1 Then
  14.         begin
  15.           IF S <> '' Then S :=S+',';
  16.           S:= S+IntToStr(I+1);
  17.           FirstIndex := I;
  18.         end;
  19.          LastIndex := I;
  20.       end else
  21.        begin
  22.          If (FirstIndex <> LastIndex) Then
  23.           Begin
  24.            S := S+'-'+IntToStr(LastIndex+1);
  25.           end;
  26.           FirstIndex := -1;
  27.           LastIndex  := -1;
  28.        end;
  29.    end;
  30.  If FirstIndex <> LastIndex Then
  31.    S:=S+'-'+IntTOstr(LastIndex+1);
  32.  caption :=S;
  33. end;                              
  34.  
Title: Re: Finding contiguous items selected in a checklistbox
Post by: maurobio on July 02, 2020, 02:30:33 am
Dear @jamie,

Thanks a lot for your code, which also works quite well!

With best wishes,
TinyPortal © 2005-2018