Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

Author Topic: [SOLVED] Finding contiguous items selected in a checklistbox  (Read 485 times)

maurobio

• Full Member
• Posts: 156
[SOLVED] Finding contiguous items selected in a checklistbox
« 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,
« Last Edit: July 02, 2020, 02:30:59 am by maurobio »
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.8 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

jamie

• Hero Member
• Posts: 3526
Re: Finding contiguous items selected in a checklistbox
« Reply #1 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.
The only true wisdom is knowing you know nothing

Handoko

• Hero Member
• Posts: 3759
• My goal: build my own game engine using Lazarus
Re: Finding contiguous items selected in a checklistbox
« Reply #2 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
« Last Edit: July 02, 2020, 12:53:59 am by Handoko »

maurobio

• Full Member
• Posts: 156
Re: Finding contiguous items selected in a checklistbox
« Reply #3 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,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.8 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

jamie

• Hero Member
• Posts: 3526
Re: Finding contiguous items selected in a checklistbox
« Reply #4 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.
The only true wisdom is knowing you know nothing

maurobio

• Full Member
• Posts: 156
Re: Finding contiguous items selected in a checklistbox
« Reply #5 on: July 02, 2020, 02:30:33 am »
Dear @jamie,

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

With best wishes,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.8 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home