• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 803
  • Last Modified:

do while nested loop logic help

I hope I can get some help in this Java zone as I am using the Salesforce apex language, but it is derived from Java.  So, the syntax should be very similar and the apex code is actually compilted on a java app engine.

Anyway, the question is how I need to structure my nested loop to the get the correct result.

Take the following example:

I have a list of the following records that I'm looping over:

8:00 AM >> Available
8:00 AM >> Available
8:15 AM >> Available
8:15 AM >> Available
8:30 AM >> Available
8:30 AM >> Available

As I loop over these records, I'm checking to see which have the same time value...eg. there are two records of each time value and if the time values are the same and the field value: ev.Screening_Close_Slots_Per_Interval__c is 1 then I would want to set the Status to Reserved for only one of the time slot values that are the same.  I would get the following result.

8:00 AM >> Reserved
8:00 AM >> Available
8:15 AM >> Reserved
8:15 AM >> Available
8:30 AM >> Reserved
8:30 AM >> Available

if the field value was 2 then I would get the following result

:00 AM >> Reserved
8:00 AM >> Reserved
8:15 AM >> Reserved
8:15 AM >> Reserved
8:30 AM >> Reserved
8:30 AM >> Reserved

My looping structure either sets all the values the reserved or all available.

Any help is appreciated.
Thanks.
List<Screening_Slot__c> ss = eventsToSlots.get(ev.Id);
				if (ss != null) {
					Integer i = 0;
					Integer j = 0;
					do {
						do {
							system.debug('*******************************we are in the inner loop');
							if (EventHelper.doConvertStringToDateTime(ss[i].Time_Slot__c) >= EventHelper.doConvertStringToDateTime(ev.Screening_Close_Start_Time__c) && EventHelper.doConvertStringToDateTime(ss[i].Time_Slot__c) <= EventHelper.doConvertStringToDateTime(ev.Screening_Close_End_Time__c)) {
								system.debug('**********************************we are in the if condition branch');
								if (i < j) {
									system.debug('**********************************i is less than j');
									ss[i].Status__c = 'Reserved';
									slotsToUpdate.add(ss[i]);
								}
							}
							j++;
							system.debug('********************************************************j='+j);
							//i++;
							system.debug('********************************************************i='+i);
						} while (j < Integer.ValueOf(ev.Screening_Close_Slots_Per_Interval__c) && ss[i].Time_Slot__c == ss[i+1].Time_Slot__c);
							i++;
							system.debug('********************************************************i='+i);
					} while (i < ss.size());		
				}

Open in new window

0
-Dman100-
Asked:
-Dman100-
  • 18
  • 9
  • 5
2 Solutions
 
CEHJCommented:
Personally i would simply sort objects of type Screening_Slot__c firstly by time, then by value. Looping through the list determining whether the second of each pair is 2 or 1 then becomes trivial and your code much cleaner
0
 
for_yanCommented:
If I understood you correctly, then
I'd think this should work if we assume that your array of times iis in String [] times and this array is sorted
and name your long fields are in field[]

String [] resavail = new String[times.length];;

String time0 = "":
for (int j=0; j<times.length;j++){
if(times[j].equals(time0) && field[j] == 1) reasavail[j] = "Reserved";
else resavail[j] = "Available";
time0 = times[j];
}

Open in new window








0
 
for_yanCommented:
You don't want to have these do whille's - you just goonce throuugh your times valaues and
keep a value of your previous time string in a special variable and compare with the previous value
if it is the same and field is equal to 1, then assign Reserved, otherwusie assign "Avaialble"
in the end assign the current value to the varioable holding previous value to preprae for the next cycle through the loop
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
-Dman100-Author Commented:
Thanks for the response and the help.  I'm not sure I've explained correctly.

The list of records is an object, which is a basically just a table with a series of fields.

So, let's say my list contains the following Screening Slot records
Time Slot >> Status >> Event Master ID
8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:30 AM >> Available >> xxxxxxxxxxxx
8:30 AM >> Available >> xxxxxxxxxxxx

Now, all of these records are a child record of a master event record

Event GUID >> Screening Close Slot Per Interval >> Start Time >> End Time
xxxxxxxxxxxx >> 1 >> 8:00 AM >> 8:30 AM

Currently, all the child records have a status of available, but when the master record is updated, I have to cycle thru the child records and look for duplidate time slots and then look at the Screening Clost Per Interval value, which in this case is 1

So, as I loop over the child records I find where the time slots are the same and then set "N" number of the duplicate times slots from "Available" to Reserved".

So, in the above example, exactly 1 of the duplicate time slots would be set to Reserved.

Let's say the list have 64 records and there were 8 duplicate values for each time slot and they were all are available and the Close Per Interval value was still one.

Then 7 of the duplicate values would be available and 1 would reserved.

if the Close Per Interval value was 2 then 6 of the duplcate values would available and 2 would be reserved,

if the Close Per Interval value was 3 then 5 of the duplicate values would be available and 3 would be reserved.

etc.

Does tha help explain?  I might not be understanding the explanation, so please let me know if I'm misunderstanding your explanation.

I really appreciate your help!
Thank you.
0
 
for_yanCommented:

Sorry, I'm slow and canot still understand:
(1) they are ordered so by duiplicate values you mean,,say, these two lines:
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
and they always come together - correct ?
 
(2) may be several of them like that:

8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx

and then you need to leave the first and cvhange
the other three?

Sorry, I'm probably still missing.



0
 
for_yanCommented:
And you also mena you have some initiial Close Per Interval  value and this valuie
is decreased by ine every time we make it reserved and when it reaches 0 we do not make
any of them reserved any longer - correct?
0
 
for_yanCommented:
Then suppose I had the list like that:

8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
...


and original Close Per Interval = 4

then I shoudl end up with such list:

8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Reserved >> xxxxxxxxxxxx
8:00 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Reserved>> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
and all the rest leave avaialable

am I right on that ?


0
 
-Dman100-Author Commented:
As for your first questions, each line is an individual child record and they should be sorted in the correct order...so, the duplicate values should all come together.  That is a good point and I need to double check that they are sorted correctly because I know what would change the logic, but from my debug statments, the results are spitting the records out in order.

As for your second question, yes, there could be many duplicate records with the same time slot as you indicated.  I can really change any of the duplicates, but it makes sense to change them in the order they are getting looped over.  So, yes, if the Close Per Interval value was 1 on the master record, then I would change the first to "Reserved" and leave the other three as Available.

If the Close Per Interval value was 2 on the master record, then I would change the first two to "Reserved" and leave the last two as Available.

If the Close Per Interval value was 3 on the master record, then I would change the first three to "
Reserved" and leave the last one as Available.

If the Close Per Interval was 4 on the master record, then I would change all four to "Reserved".

Does that help?

Thanks again for you help.
0
 
for_yanCommented:
Well, my understanding was that you don't want to change the forst one on the goroup at the same time?
Was I wrong on that?
Look at my example - so it was not right ?
0
 
for_yanCommented:
If there is no such requirement that the first time slot fro each time should stay avaialable, then why just not loop through your list and change first Close per Interval lines to Reserved ?
I'm probably again missing something

0
 
CEHJCommented:
So - the Close Per Interval on the detail records is actually unimportant in the logic, since what you do is determined by the master record?
0
 
-Dman100-Author Commented:
Hi for_yan,

In your example:

Then suppose I had the list like that:

8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
...
where the Close Per Interval is 4 you would have:

Then suppose I had the list like that:

8:00 AM >> Reserved >> xxxxxxxxxxxx
8:00 AM >> Reserved >> xxxxxxxxxxxx
8:00 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Reserved >> xxxxxxxxxxxx

Because you have three time slots at 8:00 AM and 4 time slots at 8:15 AM and the close interval is 4, so they should all be set to Reserved.

CEHJ, yes, that is correct.  The Close Per Interval is not a field on the child record so it only matters what it is on the master record.  That is the value that determines how many duplicate time slots get set to Reserved.

Thanks for taking the time to help me with this.  I sincerely appreciate it!
...
0
 
for_yanCommented:
Oh I see,
so in my case with
Close Per Interval is 4
this would become:

8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:00 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx


8:00 AM >> Reserved >> xxxxxxxxxxxx
8:00 AM >> Reserved>> xxxxxxxxxxxx
8:00 AM >> Reserved >> xxxxxxxxxxxx
8:15 AM >> Reserrved>> xxxxxxxxxxxx
8:15 AM >> Resereved >> xxxxxxxxxxxx
8:15 AM >> Reserrved >> xxxxxxxxxxxx
8:15 AM >> Reserrved >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx
8:15 AM >> Available >> xxxxxxxxxxxx

(not looking at misprints)

Hope this is correct





0
 
-Dman100-Author Commented:
yes, that is exactly correct!
0
 
for_yanCommented:
Then basically you still need to determine the moment when your time changes.
So the kind of pseudocode  will be like that
Hope it would work, unless I'm making some logical mistake

String previous_time_string = "";
int close_per_interval = num; (your given number)
int currently_left;

loop thorioough your recodrds
if(cur_record not_equals to previous_time_string) { currently_left = close_per_interval;}
if(currently_left > 0) {replace "available" to "reserverd";
currently_left--;
}
previous_time_string = cur_record;
end_of_loop
 

So you want only one loop and simpler make it for loop if you know the numvber
or maybe
while(next record is still available)
if you can do that

I don't like do whaile() lops in general



0
 
for_yanCommented:
of course I imply that each time you get new record you parse it
and cur_record is alreday piece corresponding to "8:15 AM"
as the type you like to compare them - either as String or date - whatever you prefer -
I would probably do it as strings
0
 
-Dman100-Author Commented:
I tried the logic you explained, but I might have implemented incorrectly.  See my code below.

When I tested, it set all the records to "Reserved"

I probably missed something...error on my end.
List<Screening_Slot__c> ss = eventsToSlots.get(ev.Id);
				if (ss != null) {
					String previous_time_string = '';
					Integer close_per_interval = Integer.ValueOf(ev.Screening_Close_Slots_Per_Interval__c);
					Integer currently_left = 0;
					for (Screening_Slot__c s : ss) {
						if (s.Time_Slot__c != previous_time_string) {
							currently_left = close_per_interval;
						}
						if (currently_left > 0) {
							if (EventHelper.doConvertStringToDateTime(s.Time_Slot__c) >= EventHelper.doConvertStringToDateTime(ev.Screening_Close_Start_Time__c) && EventHelper.doConvertStringToDateTime(s.Time_Slot__c) <= EventHelper.doConvertStringToDateTime(ev.Screening_Close_End_Time__c)) {	
								s.Status__c = 'Reserved';
								slotsToUpdate.add(s);
							}
							currently_left--;
						}
						previous_time_string = s.Time_Slot__c;
					}
				}

Open in new window

0
 
for_yanCommented:

you should have this line
currently_left--;

inside the if bracjes

if (currently_left > 0) {
                                          if (EventHelper.doConvertStringToDateTime(s.Time_Slot__c) >= EventHelper.doConvertStringToDateTime(ev.Screening_Close_Start_Time__c) && EventHelper.doConvertStringToDateTime(s.Time_Slot__c) <= EventHelper.doConvertStringToDateTime(ev.Screening_Close_End_Time__c)) {      
                                                s.Status__c = 'Reserved';
                                                slotsToUpdate.add(s);
currently_left--; // here

                                          }

otherwise you never decrement it and never reach zero

and then this line

previous_time_string = s.Time_Slot__c;

should be inside the big loop - not outside of it
0
 
-Dman100-Author Commented:
I moved the decrement of the currently_left variable inside the inner most if statment.

the statement:
previous_time_string = s.Time_Slot__c is inside the big loop unless I am missing something.

When I ran my code, all the records still got set to Reserved.  I'm resetting the value for previous_time_string at the end of each iteration.  Shouldn't it be:
if (s.Time_Slot__c == previous_time_string) { ...



List<Screening_Slot__c> ss = eventsToSlots.get(ev.Id);
				if (ss != null) {
					String previous_time_string = '';
					Integer close_per_interval = Integer.ValueOf(ev.Screening_Close_Slots_Per_Interval__c);
					Integer currently_left = 0;
					for (Screening_Slot__c s : ss) {
						if (s.Time_Slot__c != previous_time_string) {
							currently_left = close_per_interval;
						}
						if (currently_left > 0) {
							if (EventHelper.doConvertStringToDateTime(s.Time_Slot__c) >= EventHelper.doConvertStringToDateTime(ev.Screening_Close_Start_Time__c) && EventHelper.doConvertStringToDateTime(s.Time_Slot__c) <= EventHelper.doConvertStringToDateTime(ev.Screening_Close_End_Time__c)) {	
								s.Status__c = 'Reserved';
								slotsToUpdate.add(s);
								currently_left--;
							}
						}
						previous_time_string = s.Time_Slot__c;
					}
				}

Open in new window

0
 
for_yanCommented:
sorry, I didn't see that long line there and one bmore vbrace there
then probably prevbious position of currency_left--; was right

what is this additional if - I don't think I knoew about this check:
if (EventHelper.doConvertStringToDateTime(s.Time_Slot__c) >= EventHelper.doConvertStringToDateTime... {<-- didn't see this one

No it shoudl be like that:
if (s.Time_Slot__c != previous_time_string){
                                          currently_left = close_per_interval;
                                    }
becuae you want to make currently_left again maximum - as soon as your time has changed
not when it stays the same

In this your languge can you compare strings like that   if(s1 != s2)

In java it may bring wrong result you need to say
!s1.equals(s2)

Are you sure you can do != for objects in apex ?

0
 
-Dman100-Author Commented:
I was debugging and I think what the problem is the sorting.  The list of records is not sorted.

So, the records with the time slots are all over the place.  It could be like this:

8:00 AM
1:15 PM
11:30 AM
4:15 PM
8:00 AM
2:00 PM
10:30 AM
8:00 AM

That completely messes up the logic.  I can't sort the list becuase the list is not a list of primitive datatypes.
I can't query and use an order by because there is really no column to query on that would sort the data correctly and even if I could when it gets populated into the list, there is no guarantee that the records will be inserted into the list in the correct order.

Hmmm, any suggestions or workarounds to deal with the sorting problem?

The additional if check take compares the time slot against the new start and end time on the master record.  You might have a bunch of child records at times all throughout the day and they might want to set only a small subset of the records to Reserver for a specific time frame.  So, that logic checks to make sure the time slots that are getting set fall into the correct timeframe.

Either way, I think the sorting is the problem now.  Any ideas on how to handle that issue?
0
 
CEHJCommented:
>>Hmmm, any suggestions or workarounds to deal with the sorting problem?

Create a wrapper class for the detail object and sort instances of that. The time field should be of type Date
0
 
CEHJCommented:
If you post the code for the detail object, it would be easier to advise
0
 
for_yanCommented:
You can sort the list - write comparator which will take substring and make time oout of fiorst cjrcaters and
compare times/dates and use Collections.sort(list, comparator)
0
 
for_yanCommented:
Of course if this is not java - then it is another story.
0
 
for_yanCommented:
Do you have something like HashMap in that language ?
0
 
for_yanCommented:
if there is aome kind of map - then you
create such map where you time string would be the key and number of
ocrrences should be the value.
So you loop through all records - each time check if such key exists - if it
is not you create it - put 1 as number of currences; if it exists - you retrieve the number increment it and
put back to map. then you detrmien if this number
exceeds close_per_interval - then yo skip to the next cycle of the loop,
if not you change Avaialable to Reserved
It will be even simpler than the above code
but it is good to have something like HashMap

If youdon't have this kind of map , you'll probably need to make two
loops through your times and that would be not so
simple

0
 
-Dman100-Author Commented:
Apex does have a Map, which is a key, value pair?

Actually, the time slot is a string value, which was the datatype that was choosen prior to my arrival.  I can't change it at this point.

This is the code that populates the detail records:

It gets stored into a Map, where the master record id (Event__c) is the key and the Screening Slot record is the value.

I loop of the map values creating a new map where the key is the Event Id (Event__c), but the value is a Screening Slot list.  This is to bulkify the code.

at that point, it is stratightforward to then loop over each event (master record) and obtain the list of my child records by using the key to the map:

List<Screening_Slot__c> ss = eventsToSlots.get(ev.Id);

At which point I loop over the screening slot records, which was the above code you were both helping me with.
Map<Id, Screening_Slot__c> mapSlots = new Map<Id, Screening_Slot__c>([Select Status__c, Id, Time_Slot__c, Event__c From Screening_Slot__c Where Event__c in: inNew AND Status__c = 'Available']);
		Map<Id, List<Screening_Slot__c>> eventsToSlots = new Map<Id, List<Screening_Slot__c>>();
		for (Screening_Slot__c ss : mapSlots.values()) {
			List<Screening_Slot__c> lstSlots = eventsToSlots.get(ss.Event__c);
			if (lstSlots == null) {
				lstSlots = new List<Screening_Slot__c>();
				eventsToSlots.put(ss.Event__c, lstSlots);
			}
			lstSlots.add(ss);
		}

Open in new window

0
 
for_yanCommented:
So if it has a Map - then you can do it as I suggested above
0
 
CEHJCommented:
>>Actually, the time slot is a string value, which was the datatype that was choosen prior to my arrival.  I can't change it at this point.

You don't need to. That's the point of a wrapper class
0
 
for_yanCommented:
I think like that:

int close_per_interval = num; (your given number)
Map map - string, integer

loop thorioough your recodrds
// cur_record is your time string
int how_many = 0;
if(map conatins cur_record){
how_many = map.get(cur_record)
how_many++;
map.put(cur_record, how_many);
} else { how_many = 1;
map.put(cur_record, how_may);
}

if(how_many > close_per_interval) continue;
replace avaulable to resevre

end of loop

0
 
-Dman100-Author Commented:
Thanks for all the help!

CEHJ, I created a separate helper class file with a static method that sorted the list of sobjects.  That got the list ordered correctly.

for_yan, your example code worked and correctly set the records to Reserved based on the close per interval value.

I really appreciate the help both of your provided.
Thank you!
Regards.
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 18
  • 9
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now