1

I'm attempting to build a Visualforce table that will display the count of visible records for each user/group from a CaseShare aggregate query. I can get the results into a map, but getting the values into a table grid is hard.

My goal is to have the outer key, an ID, define the rows, the inner key, String, define the columns and the inner value, Integer, populate the cells.

TL;DR - how do I get Map<Id, Map<String, Integer>> to display as a table grid?

public Map<Id, Map<String, Integer>> resultsMap {get;set;}

public RLSViewerController(){

    List<AggregateResult> results = [SELECT UserOrGroupId users, 
    Case.Program_Name__c prog, 
    count(Id) num 
    FROM CaseShare where Case.Program_Name__c != '' 
    group by UserOrGroupId, Case.Program_Name__c];

    resultsMap = new Map<Id, Map<String, Integer>>();
    for(AggregateResult aggr : results){

        String sId = aggr.get('users').toString();
        String progName = aggr.get('prog').toString();
        Integer count = Integer.valueOf(aggr.get('num'));
        if (resultsMap.get(Id.valueOf(sId)) == null){
            resultsMap.put(Id.valueOf(sId), new Map<String, Integer>{progName => count});
        }
        else {
            resultsMap.get(Id.valueOf(sId)).put(progName, count);
        }
    }
}

A problem is that not every inner keySet is the same; some will be [A, B, C, D] and others will be [A, C]. So here's what I did

//Get all possible values for Program_Name, add to Map with zero count value
Map<String, Integer> programMap = new Map<String, Integer>();
for(Id theId : resultsMap.keyset()){
    for(String s : resultsMap.get(theId).keySet()){
        if(programMap.get(s) == null){
            programMap.put(s, 0);
        }
    }
}

//Set up outputMap with inner Map of programMap
Map<Id, Map<String, Integer>> outputMap = new Map<Id, Map<String, Integer>>();
for(Id theId : resultsMap.keySet()){
    if(outputMap.get(theId) == null){
        outputMap.put(theId, new Map<String, Integer>(programMap));
    }
}

//populate actual count values to inner Map of outputMap
for(Id theId : resultsMap.keySet()){
    outputMap.get(theId).putAll(resultsMap.get(theId));
}

So where do I go from here for getting it into a Visualforce Table? Do I need to push the map into a wrapper class? How would I dynamically create the variables (from the String) that become columns?

1

You just need to construct two lists of values, and then iterate over them. Something like this:

public Id[] userIds { get; set; }
public String[] programs { get; set; }

...

<table>
  <thead>
   <th></th>
   <apex:repeat value="{!programs}" var="program">
     <th>{!program}</th>
   </apex:repeat>
  </thead>
  <tbody>
    <apex:repeat value="{!userIds}" var="userId">
      <tr>
        <th>{!userId}</th>
        <apex:repeat value="{!programs}" var="program">
          <td>{!outputMap[userId][program]}</td>
        </apex:repeat>
      </tr>
    </apex:repeat>
  </tbody>
</table>

As you've already done in your code, make sure that every key is populated in both dimensions or you'll get a Visualforce runtime error.

  • Thanks for the pointer! This will save me a lot of time chasing dead ends! I'll post the successful code when I get it working. – AlwaysThinkin Aug 28 '18 at 12:00
1

In case someone stumbles on this question in the future, I wanted to provide the completed version to help others. This is a page to help you analyze aggregate Record Level Sharing by object and group by a field on that object.

Controller:

public with sharing class RLSViewerController {
    public Map<Id, Map<String, Integer>> resultsMap {get;set;}
    public Map<String, Integer> programMap {get; set;}
    public List<String> programList {get; set;}
    public Map<Id, Map<String, Integer>> outputMap {get; set;}
    public List<Id> idList {get; set;}


    public RLSViewerController(){

        List<AggregateResult> results = [SELECT UserOrGroupId users, 
        Case.Program_Name__c prog, 
        count(Id) num 
        FROM CaseShare where Case.Program_Name__c != '' 
        group by UserOrGroupId, Case.Program_Name__c];
        resultsMap = new Map<Id, Map<String, Integer>>();
        for(AggregateResult aggr : results){
            String sId = aggr.get('users').toString();
            String progName = aggr.get('prog').toString();
            Integer count = Integer.valueOf(aggr.get('num'));
            if (resultsMap.get(Id.valueOf(sId)) == null){
                resultsMap.put(Id.valueOf(sId), new Map<String, Integer>{progName => count});
            }
            else {
                resultsMap.get(Id.valueOf(sId)).put(progName, count);
            }
        }

        //Get all possible values for Program_Name, add to Map with zero count value
        programMap = new Map<String, Integer>();
        for(Id theId : resultsMap.keyset()){
            for(String s : resultsMap.get(theId).keySet()){
                if(programMap.get(s) == null){
                    programMap.put(s, 0);
                }
            }
        }
        //Add values from Program_name which will be used as the key for column headers
        programList = new List<String>(programMap.keySet());

        //Set up outputMap with inner Map of programMap
        outputMap = new Map<Id, Map<String, Integer>>();

        for(Id theId : resultsMap.keySet()){
            if(outputMap.get(theId) == null){
                outputMap.put(theId, new Map<String, Integer>(programMap));
            }
        }

        //Add values from userOrGroupId which will be used as the key for each row
        idList = new List<Id>(outputMap.keyset());

        //populate actual count values to inner Map of outputMap
        for(Id theId : resultsMap.keySet()){
            outputMap.get(theId).putAll(resultsMap.get(theId));
        }
    }

Visualforce Page:

<apex:page showHeader="true" controller="RLSViewerController">
<table>
        <thead>
        <th></th>
            <apex:repeat value="{!programList}" var="program">
                <th>{!program}</th>
            </apex:repeat>
        </thead>
        <tbody>
            <apex:repeat value="{!idList}" var="userId">
            <tr>
                <th>{!userId}</th>
                        <apex:repeat value="{!programList}" var="program">
                <td>{!outputMap[userId][program]}</td>
                </apex:repeat>
            </tr>
            </apex:repeat>
        </tbody>
    </table>
</apex:page>
0

It should be as simple as:

<apex:repeat value="{!map1}" var="key1">
    <apex:variable name="map2" value="{!map1[key1]}"/>
    <apex:repeat value="{!map2}" var="key2">
        {!key1} {!key2}
        {!map1[key1]}
        {!map2[key2]}
    </apex:repeat>
</apex:repeat>

I believe I have answered this before but I can't seem to find it (I answer alot of questions and don't have the time to look back through them all right now)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.