function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Kenneth KimbrellKenneth Kimbrell 

Looking to determine the best way to make a nested table with a wrapper class

My goal is to query a list of Employee Hours. I need to order by both the department the Employee works for and the Work Date. Which is the ending date for the work week. I am trying to count hours for all employees that worked within a department and during the same week period. So In other words I will have a header of each department, and under that header will be a secondary header that seperates each work date. Nested within each secondary header would be each employee that worked during that work date.

I will attach an example of what I have so far and what I am trying to accomplish.User-added image
Here is my backend logic. I can figure out the front-end for collapsing the rows. I just want to know what are some best practices people use for grouping and counting rows based on specific groupings. e.g. grouped by department and dates, then getting totals of all hours within that specific grouped date.
 
public class CLS_managerReportOfEmployeeHours {	
	public string sDate {get;set;}
	public string eDate {get;set;}
	public string bannerMessage {get;set;}
	public list<string> primary {get;set;}
	public list<string> unselected {get;set;}
	public list<string> selected {get;set;}
	public list<Hours__c> hlist {get;set;}
	public list<wrapper> wrap {get;set;}
	public boolean validError {get;set;}
	public boolean noResults {get;set;}
	
	public class wrapper{
		public date startOfWeek {get;set;}
		public date dateWorked {get;set;}
		public decimal onShoreHours {get;set;}
		public decimal offShoreHours {get;set;}
		public string department {get;set;}
		public string empName {get;set;}
		
		public wrapper(date startOfWeek,date dateWorked,decimal onShoreHours,decimal offShoreHours,string department,string empName){
			this.startOfWeek = startOfWeek;
			this.dateWorked = dateWorked;
			this.onShoreHours = onShoreHours;
			this.offShoreHours = offShoreHours;
			this.department = department;
			this.empName = empName;
		}	
	}
	
	//constructor
    public CLS_managerReportOfEmployeeHours() {
    	validError = false;
    	unselected = new list<string>(getEmployeeDepartmentList());
		selected = new list<string>();
		primary = new list<string>(getEmployeeDepartmentList());
		noResults = true;
    }
    
    public pageReference updateFormValues(){
    	getdata();
    	return null;
    }
    public void getData(){
    	date sd = date.parse(sDate).toStartOfWeek();
    	date ed = date.parse(eDate);
    	hList = new list<Hours__c>([SELECT Date_worked__c,Employee__r.Offshore__c,Employee__r.Name,Employee__r.Department__c,Employee_Name__c,hours__c,Minutes__c FROM Hours__c WHERE Date_worked__c >= :sd AND Date_worked__c <= :ed AND Employee__r.Department__c IN :selected ORDER BY Employee__r.Department__c,Date_worked__c]);
    	wrap = new list<wrapper>();
    	string tempDepartment;
    	date tempWorkDate;
    	date tempStart = sd;
    	decimal offshore = 0;
    	decimal onshore = 0;
    	for(Hours__c h: hList){
    		//count hours of both offshore and onshore
    		if(Employee__r.Offshore__c){
    			offshore += offshore;
    		}else{
    			onshore += onshore;
    		}
    		
    		//initiate a new wrapper for each department as a primary header
    		if( (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
    			tempDepartment = h.Employee__r.Department__c;
    			wrap.add(new wrapper(null,null,null,null,tempDepartment,''));
    		}
    		//initiate a new wrapper for each new week period, total employee hours for each week period for each department
    		if( (h.Date_worked__c != null && ((tempWorkDate != null && tempWorkDate != h.Date_worked__c) || ( tempWorkDate == null)) ) || (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
    			system.debug('isfiring');
    			tempWorkDate = h.Date_worked__c;
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,onshore,offshore,'total',''));
    		}
    		//initiate a new wrapper for each record and give the ability to open and collapse all records under a given total week period
			if(h.Employee__r.Offshore__c){
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,0,h.hours__c,'collapse',h.Employee__r.Name));
			}else{
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,h.hours__c,0,'collapse',h.Employee__r.Name));
			}
    	}
    	if(wrap.size() <= 0){
    		noResults = true;
    	}else{
    		noResults = false;
    	}
    	system.debug('wrap: ' + wrap);
    	system.debug('hList: ' + hList);
    }
}

Any help would be appreciated thanks.
Best Answer chosen by Kenneth Kimbrell
Kenneth KimbrellKenneth Kimbrell
public pageReference getData(){
    	date sd = date.parse(sDate).toStartOfWeek();
    	date ed = date.parse(eDate);
    	integer hitLimit = 6000;
    	decimal offshore = 0;
    	decimal onshore = 0;
    	decimal totalOffShore = 0;
    	decimal totalOnShore = 0;
    	string tempDepartment;
		string tempDep;
    	boolean dateChange;
    	boolean depChange;
    	date tempDate;
    	integer dateCount = 0;
    	//dynamic query
    	string db_select = 'SELECT Name,Employee__r.Id,Date_worked__c,Employee__r.Offshore__c,Employee__r.Name,Employee__r.Department__c,Employee_Name__c,hours__c,Minutes__c,Hours_Decimal__c';
        string db_from   = ' FROM Hours__c';
        string db_where  = ' WHERE Type_of_Hours__c = \'Work\' AND Date_worked__c >= :sd AND Date_worked__c <= :ed AND Employee__r.Department__c IN :selected AND';
        set<string> employeeTypeFilter = new set<string>();
        if(salary){
        	employeeTypeFilter.add('FT-Salaried');
        	employeeTypeFilter.add('PT-Salaried');
        }
        if(comm){
        	employeeTypeFilter.add('FT-Comm Only');
        	employeeTypeFilter.add('PT-Comm Only');
        }
        if(hourly){
        	employeeTypeFilter.add('FT-Hourly');
        	employeeTypeFilter.add('PT-Hourly');
        }
        db_where += ' Employee__r.Type__c IN :employeeTypeFilter';
        string db_orderBy = ' ORDER BY Employee__r.Department__c,Date_worked__c,Employee__r.Name';
        string db_limit = ' Limit '+ hitLimit;
        string db_query = db_select + db_from + db_where + db_orderBy + db_limit;
    	hList = new list<Hours__c>();
    	hList = database.query(db_query);
    	//end dynamic query
    	wrap = new list<wrapper>();
    	map<string, map<date, list<Hours__c>>> departmentToDateWorkedToHoursMap = new map<string, map<date, list<Hours__c>>>();
    	map<date, map<string, decimal>> dateEmpNameToAggregateTotalMap = new map<date, map<string, decimal>>();
    	map<date, list<Hours__c>> dateListHoursMap = new map<date, list<Hours__c>>();
    	map<string, decimal> EmpNameToAggregateTotalMap = new map<string, decimal>();
    	map<string, Hours__c> empToHourRecord = new map<string, Hours__c>();
    	set<string> setOfDepartments = new set<string>();
    	if(hList.size() > 0 && hList.size() < hitLimit){
        	noResults = false;
    		for(Hours__c h: hList){
    			//map one record to an employee to get employee__c relationship values
    			empToHourRecord.put(h.Employee__r.Name,h);
    			//initiate a new wrapper for each department as a primary header
	    		if( (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
	    			tempDepartment = h.Employee__r.Department__c;
	    			setOfDepartments.add(tempDepartment);
	    		}
	    		//create the mapping of hour records
	    		if(h.Date_Worked__c != null){
	    			if(!departmentToDateWorkedToHoursMap.containsKey(tempDepartment)){
	    				departmentToDateWorkedToHoursMap.put(tempDepartment,new map<date, list<Hours__c>>());
	    			}
	    			if(!dateListHoursMap.containsKey(h.Date_worked__c)) {
	    				dateListHoursMap.put(h.Date_worked__c, new list<Hours__c>());
	    			}
	    			dateListHoursMap.get(h.Date_worked__c).add(h);
	    			departmentToDateWorkedToHoursMap.put(tempDepartment,dateListHoursMap);
	    		}
	    	}
			for(string key: departmentToDateWorkedToHoursMap.keyset()){
				//iterate over departmentToDateWorkedToHoursMap and add a department header for each iteration
				wrap.add(new wrapper(null,null,null,null,null,key,'','','',null,'',''));
				for(date key2: dateListHoursMap.keyset()){
					//check if the department changed
					if(tempDep != key || tempDep == null){
						tempDep = key;
						depChange = true;
					}else{
						depChange = false;
					}
					//reset quarterly totals and date count if dateCount == 13 OR department changed
					if(depChange || dateCount == 13){
						totalOffShore = 0;
						totalOnShore = 0;
						dateCount = 0;
					}
					offshore = 0;
					onshore = 0;
					dateCount++;
					//check if dateEmpNameToAggregateTotalMap contains date, if not add a new date key and clear out the map
					if(!dateEmpNameToAggregateTotalMap.containsKey(key2)){
						dateEmpNameToAggregateTotalMap.put(key2, new map<string,decimal>());
					}
					//for each date iteraion clear out the EmpNameToAggregateTotalMap
					EmpNameToAggregateTotalMap = new map<string,decimal>();
					//iterate over only the hours records for each week period
					for(Hours__c h: dateListHoursMap.get(key2)){
						if(key == h.Employee__r.Department__c && h.Date_worked__c == key2){
							//get the total decimal hours for offshore and onshore
							if(h.Employee__r.Offshore__c){
								offshore += h.Hours_Decimal__c;
							}else{
								onshore += h.Hours_Decimal__c;
							}
							//check if EmpNameToAggregateTotalMap contains the employee name, if not add a new employee key and clear out decimal count
							if(!EmpNameToAggregateTotalMap.containsKey(h.Employee__r.Name)){
								EmpNameToAggregateTotalMap.put(h.Employee__r.Name,0);
							}
							//begin the aggreation total for EmpNameToAggregateTotalMap
							EmpNameToAggregateTotalMap.put(h.Employee__r.Name,EmpNameToAggregateTotalMap.get(h.Employee__r.Name) + h.Hours_Decimal__c);
						}
					}
					//after looping over hours record put the new values in the dateEmpNameToAggregateTotalMap
					dateEmpNameToAggregateTotalMap.put(key2, EmpNameToAggregateTotalMap);
					// begin adding the totals for each week period to create quarterly total
					totalOffShore +=  offshore;
					totalOnShore +=  onshore;
					//initiate total wrap for each week period
					wrap.add(new wrapper(key2.toStartOfWeek(),key2,onshore,offshore,null,'total','','','',null,'',''));
					//iterate over each EmpNameToAggregateTotalMap keyset for given date period and initiate a new record
					for(string key3: dateEmpNameToAggregateTotalMap.get(key2).keyset()){
						wrap.add(new wrapper(key2.toStartOfWeek(),key2,0.0,0.0,EmpNameToAggregateTotalMap.get(key3),'collapse',key3,empToHourRecord.get(key3).Employee__r.Id,'',empToHourRecord.get(key3).Employee__r.Offshore__c,'test',key2.format() ));
					}
					//initiate a quarterly total wrap once the dateCount == 13
					if(dateCount == 13){
						wrap.add(new wrapper(null,null,totalOnShore,totalOffShore,null,'quarter',key,'','',null,'',''));
					}
				}
			}
			system.debug('dateEmpNameToAggregateTotalMap: '+ dateEmpNameToAggregateTotalMap);
    		system.debug('setOfDepartments: '+ setOfDepartments);
    		system.debug('departmentToDateWorkedToHoursMap: '+ departmentToDateWorkedToHoursMap);
    		system.debug('dateListHoursMap: '+ dateListHoursMap);
    	}else if(hList.size() <= 0){
    		noResults = true;
    		bannerMessage = 'Oops! No record found.';
    	}else if(hList.size() >= hitLimit){
    		noResults = true;
    		bannerMessage = 'Oops! 6000 Record limit hit.';
    	}
    	system.debug('wrap: ' + wrap);
    	system.debug('hList: ' + hList);
    	return null;
    }
I want to update my answer. I discovered that running a bunch of nested for loops was not a good idea, when dealing with large data queries. I decided to go for a more effecient approach with mapping and it worked quite well.
 

All Answers

Raj VakatiRaj Vakati
You need to create a wrapper class with innner range like below  Refer the below code
 
 
public class CLS_managerReportOfEmployeeHours {	
	public string sDate {get;set;}
	public string eDate {get;set;}
	public string bannerMessage {get;set;}
	public list<string> primary {get;set;}
	public list<string> unselected {get;set;}
	public list<string> selected {get;set;}
	public list<Hours__c> hlist {get;set;}
	public list<wrapper> wrap {get;set;}
	public boolean validError {get;set;}
	public boolean noResults {get;set;}
	//  DO an Repeat on this one 
	public class Header {
			public string headerName {get;set;}
			public List<wrapper> tableRows{get;set;}
		
	}
	
	public class wrapper{
		public date startOfWeek {get;set;}
		public date dateWorked {get;set;}
		public decimal onShoreHours {get;set;}
		public decimal offShoreHours {get;set;}
		public string department {get;set;}
		public string empName {get;set;}
		
		public wrapper(date startOfWeek,date dateWorked,decimal onShoreHours,decimal offShoreHours,string department,string empName){
			this.startOfWeek = startOfWeek;
			this.dateWorked = dateWorked;
			this.onShoreHours = onShoreHours;
			this.offShoreHours = offShoreHours;
			this.department = department;
			this.empName = empName;
		}	
	}
	
	//constructor
    public CLS_managerReportOfEmployeeHours() {
    	validError = false;
    	unselected = new list<string>(getEmployeeDepartmentList());
		selected = new list<string>();
		primary = new list<string>(getEmployeeDepartmentList());
		noResults = true;
    }
    
    public pageReference updateFormValues(){
    	getdata();
    	return null;
    }
    public void getData(){
    	date sd = date.parse(sDate).toStartOfWeek();
    	date ed = date.parse(eDate);
    	hList = new list<Hours__c>([SELECT Date_worked__c,Employee__r.Offshore__c,Employee__r.Name,Employee__r.Department__c,Employee_Name__c,hours__c,Minutes__c FROM Hours__c WHERE Date_worked__c >= :sd AND Date_worked__c <= :ed AND Employee__r.Department__c IN :selected ORDER BY Employee__r.Department__c,Date_worked__c]);
    	wrap = new list<wrapper>();
    	string tempDepartment;
    	date tempWorkDate;
    	date tempStart = sd;
    	decimal offshore = 0;
    	decimal onshore = 0;
    	for(Hours__c h: hList){
    		//count hours of both offshore and onshore
    		if(Employee__r.Offshore__c){
    			offshore += offshore;
    		}else{
    			onshore += onshore;
    		}
    		
    		//initiate a new wrapper for each department as a primary header
    		if( (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
    			tempDepartment = h.Employee__r.Department__c;
    			wrap.add(new wrapper(null,null,null,null,tempDepartment,''));
    		}
    		//initiate a new wrapper for each new week period, total employee hours for each week period for each department
    		if( (h.Date_worked__c != null && ((tempWorkDate != null && tempWorkDate != h.Date_worked__c) || ( tempWorkDate == null)) ) || (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
    			system.debug('isfiring');
    			tempWorkDate = h.Date_worked__c;
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,onshore,offshore,'total',''));
    		}
    		//initiate a new wrapper for each record and give the ability to open and collapse all records under a given total week period
			if(h.Employee__r.Offshore__c){
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,0,h.hours__c,'collapse',h.Employee__r.Name));
			}else{
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,h.hours__c,0,'collapse',h.Employee__r.Name));
			}
    	}
    	if(wrap.size() <= 0){
    		noResults = true;
    	}else{
    		noResults = false;
    	}
    	system.debug('wrap: ' + wrap);
    	system.debug('hList: ' + hList);
    }
}



 
Kenneth KimbrellKenneth Kimbrell
I am sorry Raj, can you provide more detail or an example document?
 
public class Header {
	public string headerName {get;set;}
	public List<wrapper> tableRows{get;set;}	
}
to me all this would imply is a way to potentially make the first level header which is the employee department name. I have already achieved this by ordering the list of Hours__c by Department. That way the Employee__r.department is already being queried in order. So I then use conditions to see when the department name changes. once it changes I then instantiate a new wrapper that identifies the name of the header and I use front-end logic to hide or show a row based on the value of the department name. The result is having the hours records grouped by department and the ability for the user to see that on the front end. Problem is that is where my logic stops. The ability to keep grouped dates with the grouped departments only works to a small extent. When I add more departments to the filter it no longer works as expected. 

Can you let me know why creating an inner range and nesting my wrapper class into another wrapper class will help me achieve my desired functionality?

Thanks for your help.
Raj VakatiRaj Vakati
What i am saying it 
  1. Genarted the header aslo into the wrapper class  --> Set it to headerName  var in Header wrapper class 
  2. Genarted list of records into the tableRows variable of Header wrapper class  
  3. Final result wil be List<Header>  wrapper class 
  4. Perfom iteration on List<Header >
Kenneth KimbrellKenneth Kimbrell

I got it to work a different way. Here is the result if someone is interested in nesting tables differently.

public void getData(){
    	date sd = date.parse(sDate).toStartOfWeek();
    	date ed = date.parse(eDate);
    	hList = new list<Hours__c>([SELECT Name,Employee__r.Id,Date_worked__c,Employee__r.Offshore__c,Employee__r.Name,Employee__r.Department__c,Employee_Name__c,hours__c,Minutes__c FROM Hours__c WHERE Date_worked__c >= :sd AND Date_worked__c <= :ed AND Employee__r.Department__c IN :selected ORDER BY Employee__r.Department__c,Date_worked__c]);
    	wrap = new list<wrapper>();
    	string tempDepartment;
    	date tempWorkDate;
    	date tempStart = sd;
    	decimal offshore = 0;
    	decimal onshore = 0;
    	decimal totalOffshore = 0;
    	decimal totalOnShore = 0;
    	decimal dateCount = 0;
    	boolean fired = false;
    	boolean dateCountis13 = false;
    	for(Hours__c h: hList){
    		//initiate a new wrapper for each department as a primary header
    		if( (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
    			fired = true;
    			tempDepartment = h.Employee__r.Department__c;
    			wrap.add(new wrapper(null,null,null,null,null,tempDepartment,'','','',null));
    		}else{
    			fired = false;
    		}
    		//initiate a new wrapper for each new week period, total employee hours for each week period for each department
    		if(fired || (h.Date_worked__c != null && ((tempWorkDate != null && tempWorkDate != h.Date_worked__c) || ( tempWorkDate == null)) ) || (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
    			system.debug('isfiring');
    			tempWorkDate = h.Date_worked__c;
    			offshore = 0;
    			onshore = 0;
    			if(fired || dateCountis13){
    				dateCountis13 = false;
    				dateCount = 0;
    				totalOffshore = 0;
    				totalOnShore = 0;
    			}
    			dateCount++;
    			for(Hours__c h1: hList){
    				//logic for totaling offshore hours by date and department
    				if(h1.Date_worked__c == tempWorkDate && h1.Employee__r.Offshore__c && h1.Employee__r.Department__c == tempDepartment){
    					offshore += h1.hours__c;
    				}
    				//logic for totaling onshore hours by date and department
    				if(h1.Date_worked__c == tempWorkDate && h1.Employee__r.Offshore__c == false && h1.Employee__r.Department__c == tempDepartment){
    					onshore += h1.hours__c;
    				}
    			}
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,onshore,offshore,null,'total','','','',null));
    		}
    		if(h.Employee__r.Offshore__c){
				totalOffshore += h.hours__c;
			}else{
				totalOnShore += h.hours__c;
			}
    		//if on the 13th week -- initiate a new wrapper and and get quarterly totals
    		if(dateCount == 13){
    			dateCountis13 = true;
    			wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,totalOnShore,totalOffshore,null,'quarter',tempDepartment,'','',null));
			}
    		//initiate a new wrapper for each record and give the ability to open and collapse all records under a given total week period
			if(h.Employee__r.Offshore__c){
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,0,h.hours__c,h.hours__c,'collapse',h.Employee__r.Name,h.Employee__r.Id,h.Name,h.Employee__r.Offshore__c));
			}else{
				wrap.add(new wrapper(h.Date_worked__c.toStartOfWeek(),h.Date_worked__c,h.hours__c,0,h.hours__c,'collapse',h.Employee__r.Name,h.Employee__r.Id,h.Name,h.Employee__r.Offshore__c));
			}
    	}
    	if(wrap.size() <= 0){
    		noResults = true;
    	}else{
    		noResults = false;
    	}
    	system.debug('wrap: ' + wrap);
    	system.debug('hList: ' + hList);
    }
Kenneth KimbrellKenneth Kimbrell
public pageReference getData(){
    	date sd = date.parse(sDate).toStartOfWeek();
    	date ed = date.parse(eDate);
    	integer hitLimit = 6000;
    	decimal offshore = 0;
    	decimal onshore = 0;
    	decimal totalOffShore = 0;
    	decimal totalOnShore = 0;
    	string tempDepartment;
		string tempDep;
    	boolean dateChange;
    	boolean depChange;
    	date tempDate;
    	integer dateCount = 0;
    	//dynamic query
    	string db_select = 'SELECT Name,Employee__r.Id,Date_worked__c,Employee__r.Offshore__c,Employee__r.Name,Employee__r.Department__c,Employee_Name__c,hours__c,Minutes__c,Hours_Decimal__c';
        string db_from   = ' FROM Hours__c';
        string db_where  = ' WHERE Type_of_Hours__c = \'Work\' AND Date_worked__c >= :sd AND Date_worked__c <= :ed AND Employee__r.Department__c IN :selected AND';
        set<string> employeeTypeFilter = new set<string>();
        if(salary){
        	employeeTypeFilter.add('FT-Salaried');
        	employeeTypeFilter.add('PT-Salaried');
        }
        if(comm){
        	employeeTypeFilter.add('FT-Comm Only');
        	employeeTypeFilter.add('PT-Comm Only');
        }
        if(hourly){
        	employeeTypeFilter.add('FT-Hourly');
        	employeeTypeFilter.add('PT-Hourly');
        }
        db_where += ' Employee__r.Type__c IN :employeeTypeFilter';
        string db_orderBy = ' ORDER BY Employee__r.Department__c,Date_worked__c,Employee__r.Name';
        string db_limit = ' Limit '+ hitLimit;
        string db_query = db_select + db_from + db_where + db_orderBy + db_limit;
    	hList = new list<Hours__c>();
    	hList = database.query(db_query);
    	//end dynamic query
    	wrap = new list<wrapper>();
    	map<string, map<date, list<Hours__c>>> departmentToDateWorkedToHoursMap = new map<string, map<date, list<Hours__c>>>();
    	map<date, map<string, decimal>> dateEmpNameToAggregateTotalMap = new map<date, map<string, decimal>>();
    	map<date, list<Hours__c>> dateListHoursMap = new map<date, list<Hours__c>>();
    	map<string, decimal> EmpNameToAggregateTotalMap = new map<string, decimal>();
    	map<string, Hours__c> empToHourRecord = new map<string, Hours__c>();
    	set<string> setOfDepartments = new set<string>();
    	if(hList.size() > 0 && hList.size() < hitLimit){
        	noResults = false;
    		for(Hours__c h: hList){
    			//map one record to an employee to get employee__c relationship values
    			empToHourRecord.put(h.Employee__r.Name,h);
    			//initiate a new wrapper for each department as a primary header
	    		if( (h.Employee__r.Department__c != null && ((tempDepartment != null && tempDepartment != h.Employee__r.Department__c) || ( tempDepartment == null)) ) ){
	    			tempDepartment = h.Employee__r.Department__c;
	    			setOfDepartments.add(tempDepartment);
	    		}
	    		//create the mapping of hour records
	    		if(h.Date_Worked__c != null){
	    			if(!departmentToDateWorkedToHoursMap.containsKey(tempDepartment)){
	    				departmentToDateWorkedToHoursMap.put(tempDepartment,new map<date, list<Hours__c>>());
	    			}
	    			if(!dateListHoursMap.containsKey(h.Date_worked__c)) {
	    				dateListHoursMap.put(h.Date_worked__c, new list<Hours__c>());
	    			}
	    			dateListHoursMap.get(h.Date_worked__c).add(h);
	    			departmentToDateWorkedToHoursMap.put(tempDepartment,dateListHoursMap);
	    		}
	    	}
			for(string key: departmentToDateWorkedToHoursMap.keyset()){
				//iterate over departmentToDateWorkedToHoursMap and add a department header for each iteration
				wrap.add(new wrapper(null,null,null,null,null,key,'','','',null,'',''));
				for(date key2: dateListHoursMap.keyset()){
					//check if the department changed
					if(tempDep != key || tempDep == null){
						tempDep = key;
						depChange = true;
					}else{
						depChange = false;
					}
					//reset quarterly totals and date count if dateCount == 13 OR department changed
					if(depChange || dateCount == 13){
						totalOffShore = 0;
						totalOnShore = 0;
						dateCount = 0;
					}
					offshore = 0;
					onshore = 0;
					dateCount++;
					//check if dateEmpNameToAggregateTotalMap contains date, if not add a new date key and clear out the map
					if(!dateEmpNameToAggregateTotalMap.containsKey(key2)){
						dateEmpNameToAggregateTotalMap.put(key2, new map<string,decimal>());
					}
					//for each date iteraion clear out the EmpNameToAggregateTotalMap
					EmpNameToAggregateTotalMap = new map<string,decimal>();
					//iterate over only the hours records for each week period
					for(Hours__c h: dateListHoursMap.get(key2)){
						if(key == h.Employee__r.Department__c && h.Date_worked__c == key2){
							//get the total decimal hours for offshore and onshore
							if(h.Employee__r.Offshore__c){
								offshore += h.Hours_Decimal__c;
							}else{
								onshore += h.Hours_Decimal__c;
							}
							//check if EmpNameToAggregateTotalMap contains the employee name, if not add a new employee key and clear out decimal count
							if(!EmpNameToAggregateTotalMap.containsKey(h.Employee__r.Name)){
								EmpNameToAggregateTotalMap.put(h.Employee__r.Name,0);
							}
							//begin the aggreation total for EmpNameToAggregateTotalMap
							EmpNameToAggregateTotalMap.put(h.Employee__r.Name,EmpNameToAggregateTotalMap.get(h.Employee__r.Name) + h.Hours_Decimal__c);
						}
					}
					//after looping over hours record put the new values in the dateEmpNameToAggregateTotalMap
					dateEmpNameToAggregateTotalMap.put(key2, EmpNameToAggregateTotalMap);
					// begin adding the totals for each week period to create quarterly total
					totalOffShore +=  offshore;
					totalOnShore +=  onshore;
					//initiate total wrap for each week period
					wrap.add(new wrapper(key2.toStartOfWeek(),key2,onshore,offshore,null,'total','','','',null,'',''));
					//iterate over each EmpNameToAggregateTotalMap keyset for given date period and initiate a new record
					for(string key3: dateEmpNameToAggregateTotalMap.get(key2).keyset()){
						wrap.add(new wrapper(key2.toStartOfWeek(),key2,0.0,0.0,EmpNameToAggregateTotalMap.get(key3),'collapse',key3,empToHourRecord.get(key3).Employee__r.Id,'',empToHourRecord.get(key3).Employee__r.Offshore__c,'test',key2.format() ));
					}
					//initiate a quarterly total wrap once the dateCount == 13
					if(dateCount == 13){
						wrap.add(new wrapper(null,null,totalOnShore,totalOffShore,null,'quarter',key,'','',null,'',''));
					}
				}
			}
			system.debug('dateEmpNameToAggregateTotalMap: '+ dateEmpNameToAggregateTotalMap);
    		system.debug('setOfDepartments: '+ setOfDepartments);
    		system.debug('departmentToDateWorkedToHoursMap: '+ departmentToDateWorkedToHoursMap);
    		system.debug('dateListHoursMap: '+ dateListHoursMap);
    	}else if(hList.size() <= 0){
    		noResults = true;
    		bannerMessage = 'Oops! No record found.';
    	}else if(hList.size() >= hitLimit){
    		noResults = true;
    		bannerMessage = 'Oops! 6000 Record limit hit.';
    	}
    	system.debug('wrap: ' + wrap);
    	system.debug('hList: ' + hList);
    	return null;
    }
I want to update my answer. I discovered that running a bunch of nested for loops was not a good idea, when dealing with large data queries. I decided to go for a more effecient approach with mapping and it worked quite well.
 
This was selected as the best answer