Showing posts with label methods. Show all posts
Showing posts with label methods. Show all posts

Thursday, February 2, 2012

Detecting the sObject type of Id's from ambiguous fields

When dealing with the OwnerId field, it is important to check if it is a User or a Group sObject you are dealing with before you start using it or you'll get errors. The Task object also has a few ambiguous fields that match up multiple Objects like the WhoId and the WhatId. In triggers and in other logic you will sometimes need to detect what the object is to either exclude or include it in the next bit of code. We dont want to iterate through code and update records if we don't need to, it will bog down the system. 


I have seen some of the ways out there that are used, particularly the one where you check if the first 3 letters of the ID is '500' or '300' but i do not think this is an accurate way of detection. I want one that the system tells me for sure what object it is. Unfortunately its not as dynamic as i would like it to be due to limitations in APEX, but it is still a much better detection than the above. 


I first made a helper class called GlobalHelper_IsSobjectType and populated it with a few methods that look like the following. It takes in the Id and after a check to make sure that an Id was actually passed and is not null, it try's to create an Object of that Type. If it can it will not fall into the Catch and will return true, other wise it will error and return false, but it will not error in a way that will halt your program from finishing(thus why its in a try catch). 




I made one for Account, Contact, Lead, User, and Opportunity since those are the ones I typically have to detect. When it comes to the OwnerId field I can just use the one for User since if it returns false i know its a group. I also found that I needed to Overload the method so it would work with string as well as Id: 




Ideally I was hoping to make the above but make it more dynamic so you pass the object type you wanted to detect as well as the id for the detecting. That way you would only need one method instead of one for each Object, but I have yet to find a way to make it work. Now of course you need to test the code, and I prefer as close to 100% coverage as possible so:






Questions?  
Twitter: @SalesForceGirl  or Facebook


Tuesday, May 31, 2011

What makes Elegant Code?

One of the things i love about programming is that there is always several different ways to accomplish a task. If you gave 10 programmers the same task, each would go about it in there own way, sure the result would be the same but the code will be completely unique.

I find that when i look at other programmers code, its almost like looking at an artist work, not to say that the code is a work of art, just unique and sometimes beautiful. Every programmer has there own way of doing things, tendencies that all make up the big picture of the code. The naming convention used, structure of the methods and even the way they went about accomplishing the task. It all contributes to the look and feel of the code. It also can show the skill of the programmer or artist, or lack there of. 

Code should be simplistic, elegant, beautiful. It should also be structured, and still 'flow' like a picture. So many times i look at code and often think of spaghetti, and cringe at the over complication of something that should only be one or two lines and not ten. And please don't get me wrong, i am always trying to learn new ways to improve my code, trying to find smarter ways to go about a problem, keeping an open mind to new ideas and concepts.

An example of what i mean, below is two lines, both of the same SOQL statement, both get the job done. But one, while not wrong, also isn't quite right.

Contact[] objContact = [select id, name from Contact where email ='someemail@email.com' limit 1];

Contact objContact = [select id, name from Contact where email = :sInEmail limit 1];

In the first SOQL statement, it is creating a list of Contacts even though the query is only returning one. It also has a 'hard-coded' variable which is never a good practice.  In the second, since we are only querying for one obj, it only creates a single object, also it uses a variable instead of string text. 

Now i know what you may be thinking, does that really make a difference in the elegance of the code? Yes. If we left it as the first statement, where it is returning a List of objs instead of a single obj, and all the proceeding methods would then have to accommodate for it, even thought it isn't necessary. Its like adding lines and logic that only complicate and so the code tends to look messy or clustered when it doesn't need to be. Keeping code simplistic is key. As a rule of thumb, I try to keep my methods under 10 lines total, and that includes all the curly brackets.

Here is another example of doing things different ways while still accomplishing the same task:

      public string mystring;

      public string getMystring()
      {
            return mystring;
      }

      public void setMyString(string s)
      {
            this.s = mystring;
      }

vs

          public string sInStringName{get;set;}

I hope I don't need to explain why the second is cleaner, but if you notice i use the variable name of 'sInStringName' instead of 'mystring', i do this for most variable's: 'lst' for list, 'obj' for sObjects, etc. this helps me identify what it is, along with what type. So if i am looking for a string, i know it will start with 's'. I also like to follow the camel hump approach where the first word in the name is lowercase and all words after have the first letter capitalized. When it comes to booleans i am still trying to decide if i like 'b' as in 'bInBoolean' or 'is' since it is a true false statement, which would look like 'isBoolean'. Its important that no matter what naming convention you choose, that you stick with it, for the whole project, if you don't like it, try tweaking it in the next project.

Questions? 

Twitter: @SalesForceGirl  or Facebook

Tuesday, April 26, 2011

Google User Org-Chart with Salesforce

Cloudspokes.com had a challenge to incorporate Google's user hierarchy chart in Salesforce on the user object. And even though I didn't win (totally should have lol) I thought I would share my version of the challenge.  
In mine, the Hierarchy chart is based off of the Manager Id of each User, instead of going into Roles and all that mess. But when I set it up for all users, THe chart produced was very long and made the page scroll, to fix that issue, I made it so it grouped by department, and by default show the current User's Department. Then I added a fixed width on the page so it wouldn't scroll, except inside the frame, and if the department drop-down is on the page it would always show on top right regardless of how long the chart is that gets created. Since not all managers where in the same department as their subordinates, I had to re-query for any managers that weren't in the list the of the users returned, other wise it showed as only an ID above the user, which looked bad lol.
I have set the page up to take a few different params(see below) which control options for the page, including hiding/showing a User table, a select list for departments, and the hiding/showing of the sidebar and header. 
Additional options lie in the JavaScript, there you have the ability to allow people to click on a user and see additional info on them(works/looks really good on iPad). Or control the Google chart options like allow HTML and the collapse feature. To see the  'onclick additional info' in action simply click once on a users name and it will pop up with a small modal of that info.

When trying to make this work, i found that apex and Google do not play nice, especially with re-render, so i had to make a lot of custom JavaScript to make it all work correctly, and i could not use Apex:output or have the JS in the re-render, otherwise it would spit out all the code to the page on re-render. 

Here is the JS
/*SALESFORCEGIRL ORGCHAT 2011*/

/*CONTROLS THE LOADING GIF*/
function Loading(b){
     if(b){
          $('.loadingIcon').removeClass('hideIt');
     }else{
          $('.loadingIcon').addClass('hideIt');
     }
}
/*PUTS THE USER LIST IN THE RIGHT FORMAT WITH OPTIONS FOR GOOGLE TO READ*/
function getUserList(userOptions, CurrentUserId, dptLabel,phLabel,rxtLabel,emLabel) {
     var users = [];
     var UserString = '';
     var uId = '';
     var uName = '';
     var uManId = '';
     var uDept = '';
     var uPhone = '';
     var uExt = '';
     var uEmail = '';                 
     $('#hiddenUserData tr.dataRow').each(function(){
          uId = $(this).find('td:eq(0)').text();
          uManId = $(this).find('td:eq(1)').text();
          uName = $(this).find('td:eq(2)').text();
          uDept = $(this).find('td:eq(3)').text();
          uPhone = $(this).find('td:eq(4)').text();
          uExt = $(this).find('td:eq(5)').text();
          uEmail = $(this).find('td:eq(6)').text();
      
          UserString = '<div class="UserInfo" onClick="ShowMoreInfo(this)">'+uName+'<br /><div class="Title" >'+uDept+'</div><br />';
      
          if (userOptions.showInfo.department || userOptions.showInfo.phone || userOptions.showInfo.ext || userOptions.showInfo.email) {
               UserString += '<div title="Click to hide" class="additionalInfo hideMe"><table><tbody><tr><td colspan="2">';
           
               UserString += '<div class="HeaderTitle">'+uName+'</div></td></tr>';
           
               if (userOptions.showInfo.department) {
                    UserString += '<tr><th>'+dptLabel+'</th><td>'+uDept+'</td></tr>';
               }
           
               if (userOptions.showInfo.phone) {
                    UserString += '<tr><th>'+phLabel+'</th><td>'+uPhone+'</td></tr>';
               }
           
               if (userOptions.showInfo.ext) {
                    UserString += '<tr><th>'+rxtLabel+'</th><td>'+uExt+'</td></tr>';
               }
           
               if (userOptions.showInfo.email) {
                    UserString += '<tr><th>'+emLabel+'</th><td>'+uEmail+'</td></tr>';
               }
           
               UserString += '</tbody></table></div>';
          }
          UserString += '</div>';
 
          users.push([{v: uId, f: UserString}, uManId, 'Click for additional info']);
     });                     
     return users;
}
/*USED TO TRIGGER THE ADDITIONAL INFO POP UP */
function Loading(b){
     if(b){
          $('.loadingIcon').removeClass('hideIt');
     }else{
          $('.loadingIcon').addClass('hideIt');
     }
}
/*USED TO SHOW MORE INFO IF USER IS CLICKED*/
function ShowMoreInfo(t){
     var item = $(t).find('.additionalInfo');
     if($(item).hasClass('hideMe')){
          $('.additionalInfo').addClass('hideMe');
          $(item).removeClass('hideMe');
     }else{
          $(item).addClass('hideMe');
     }
}
/*SETTING UP THE GOOGLE CHART*/
function initializeOrgChart(){
     google.load('visualization', '1', {packages:['orgchart']});
}
/*SETTING UP THE GOOGLE CHART*/
function getUserDataTable(userList){
     var dt = new google.visualization.DataTable();
       dt.addColumn('string', 'Name');
       dt.addColumn('string', 'Manager');
       dt.addColumn('string', 'ToolTip');
       dt.addRows(userList);
       return dt;
}
/*SETTING UP THE GOOGLE CHART*/
function getOrgChart(container,options,data){
     var chart = new google.visualization.OrgChart(container);
       chart.draw(data, options);
     return chart;
}
/*SETTING UP THE GOOGLE CHART*/
function drawOrgChart(container,options) {         
     /* DEFAULT OPTIONS STRUCTURE.
     var options = {
          users: null,                    
          chartOptions: {
               allowHtml: true,
               allowCollapse:true
          }
     };*/
       var data  = getUserDataTable(options.users);
       var chart = getOrgChart(container,options.chartOptions,data);
}

Then on the page:
<apex:page controller="sfgOrgChart" title="Org Chart" showHeader="{!bShowHeader}" sidebar="{!bShowSidebar}" standardstylesheets="false" action="{!SwitchList}">
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
        <apex:stylesheet value="{!URLFOR($Resource.OrgChart, '/style.css')}"/>
       
        <script type="text/javascript" language="javascript" src="{!URLFOR($Resource.OrgChart, '/js/jquery-1.4.2.min.js')}" ></script>
        <script type="text/javascript" language="javascript" src="{!URLFOR($Resource.OrgChart, '/js/jquery.tablesorter.min.js')}" ></script>
        <script type="text/javascript" language="javascript" src="{!URLFOR($Resource.OrgChart, '/js/sfgOrgChart.js')}" ></script>
        <script type='text/javascript' src='https://www.google.com/jsapi'></script>
    </head>
    <body>
        <apex:form id="form">
            <apex:outputpanel id="pageWrap">
                <apex:outputpanel id="ChooseDepartmentWrap" rendered="{!bShowDepartmentselectList}">
                    <div class="clear"></div>
                    <br />
                    <div class="floatL">
                        <apex:outputpanel rendered="{!NOT(ISBLANK(sDepartment))}">
                            <div class="HeaderTitle">Viewing Department:&nbsp;<apex:outputtext value="{!IF(bIsDept,sDepartment,'All Departments')}" /></div>
                        </apex:outputpanel>
                    </div>
                    <div class="floatR">
                        <apex:outputpanel rendered="{!bShowDepartmentSelectList}">
                            <strong>{!$ObjectType.User.Fields.Department.Label}:</strong>
                            <apex:selectList id="department" size="1" value="{!sDepartment}" styleClass="deptSelect"> 
                                <apex:selectOptions value="{!lstDepartmentOptions}" />
                                <apex:actionSupport event="onchange" onsubmit="Loading(true);$('#chart_div').html('');" action="{!SwitchList}" rerender="pageWrap" id="actionSupportForDeptartment" oncomplete="drawOrgChart($('#chart_div')[0],Go('{!$User.Id}'));ShowTableChart();Loading(false);" />
                            </apex:selectList>
                        </apex:outputpanel>
                    </div>
                    <!-- the loading icon -->
                    <div class="loadingIcon hideIt"><apex:image id="loadingImage" value="{!URLFOR($Resource.OrgChart, 'images/loader_24x26.gif')}" width="24" height="26"/></div>
                    <div class="clear"></div>
                </apex:outputpanel>
                <div id="chart_div"></div>
                <br />
                <div>
                    <table id="hiddenUserData" class="list tablesorter hideMe">
                        <thead class="rich-table-thead">
                        <tr class="headerRow">
                            <th colspan="1" scope="col">Id</th>
                            <th colspan="1" scope="col">Manager Id</th>
                            <th colspan="1" scope="col">Name</th>
                            <th colspan="1" scope="col">Department</th>
                            <th colspan="1" scope="col">Phone</th>
                            <th colspan="1" scope="col">Ext</th>
                            <th colspan="1" scope="col">Email</th>
                        </tr>
                        </thead>
                        <tbody>
                        <apex:repeat value="{!lstOfUsers}" var="u"><!-- Table to get the data from and to show if they wish it with sorting -->
                            <tr class="dataRow" id="{!u.id}">
                                <td>{!JSENCODE(u.id)}</td>
                                <td>{!JSENCODE(u.ManagerId)}</td>
                                <td>{!JSENCODE(u.name)}</td>
                                <td>{!JSENCODE(u.department)}</td>
                                <td>{!JSENCODE(u.phone)}</td>
                                <td>{!JSENCODE(u.extension)}</td>
                                <td>{!JSENCODE(u.email)}</td>
                            </tr>
                        </apex:repeat>
                    </tbody></table>
                </div>
        </apex:outputpanel>
        </apex:form>
        <script type="text/javascript" language="javascript">
          
            initializeOrgChart();  // LOAD API IMPORTANT TO DO THIS.          
            var ShowTable = {!bShowUserTable};
            var dptLabel = '{!$ObjectType.User.Fields.Department.Label}';
            var phLabel = '{!$ObjectType.User.Fields.Phone.Label}';
            var rxtLabel = '{!$ObjectType.User.Fields.Extension.Label}';
            var emLabel = '{!$ObjectType.User.Fields.Email.Label}';
          
            var winWidth = $(window).width() - 75 +'px !important;overflow-y:hidden;';
            var isiPad = navigator.userAgent.match(/iPad/i) != null;
            if(isiPad){
                winWidth = '100% !important;'
            }
              
            function Go(UserId){
              
                // THE ADDITIONAL INFO OPTIONS
               // IF ALL ARE FALSE WILL NOT SHOW ADDITIONAL INFO
                var userOptions = {
                      showInfo: {
                            department: true,      // show dept in additional info box
                            phone: true,           // show phone in additional info box
                            ext: true,             // show ext in additional info box
                            email: true            // show email in additional info box
            }
                };       
           //GOOGLE CHART OPTIONS
           var options = {
                 users: getUserList(userOptions,UserId, dptLabel,phLabel,rxtLabel,emLabel),
                 chartOptions: {               // default google chart options
                       allowHtml: true,        // allow html
                       allowCollapse:true      // allow collapsing 
                 }
           };                

            function ShowTableChart(){
                if(ShowTable){
                    $("table").tablesorter({
                        headers: {
                            0: {sorter: 'text'},
                            1: {sorter: 'text'},
                            2: {sorter: 'text'},
                            3: {sorter: 'text'},
                            4: {sorter: 'digit'},
                            5: {sorter: 'digit'},
                            6: {sorter: 'text'}
                        }
                    });
                    $('#hiddenUserData').removeClass('hideMe');
                }         
            }
            $(document).ready(function(){                 
                //DRAW CHART WITH DATA AND MAKE SURE IT WONT OVERFLOW
                $('#chart_div').attr('style','width:'+winWidth+' margin:5px auto; padding:10px 5px;');
                drawOrgChart($('#chart_div')[0],Go('{!$User.Id}'));
                $('.deptSelect').val('{!sDepartment}');
                //IF SHOW-TABLE START TABLE SORTER AND SHOW TABLE
                ShowTableChart();
            });
        </script>
    </body>
    </html>

</apex:page>
Example: If the dept param(URL_KEY_SHOWDEPT) is passed with URL_DEFAULT_VALUE_SHOWALL it will show all Users under all Departments, and if SHOW_SELECT_DEPT is set to true, then it will also show a Department select list so it is easy to change between departments. If you click on a User in the Chart it will display additional information on that user including: Name, Phone, Title, Email, Department, Extension. This can be turned off/on and/or you can set which info shows by customizing the JavaScript options. If all options are false, then the additional info will not show. 

NOTE: Department select list will only show if a param is passed and the SHOW_SELECT_DEPT is true...this is not the same for the other params.. see below

In the Common class there are a few variables that you can change to change the defaults of the page:
     
     // keys to look for in the url
     public static final string URL_KEY_SHOWDEPT = 'dept'; 
     public static final string URL_KEY_SHOWSIDEBAR = 'side';
     public static final string URL_KEY_SHOWHEADER = 'top';
     public static final string URL_KEY_SHOWUSER = 'user';

     // the show all param value
     public static final string URL_DEFAULT_VALUE_SHOWALL = 'all';

     // if true and param is passed will show select list 
     public static final boolean SHOW_SELECT_DEPT = true;

     // limit on the user Query
     public static final integer USER_SEARCH_LIMIT = 400;

     // show the header
     public static final boolean SHOW_HEAD = true;

     // show the side bar
     public static final boolean SHOW_SIDE = false;

     // show the table of users currently in the google charts
     public static final boolean SHOW_USER_LIST = true;

EXAMPLE URLs:

-shows Users by current users dept (nothing was passed so it defaults to Current Users Dept)
-shows header (nothing was passed so it defaults to SHOW_HEAD )
-hides sidebar (nothing was passed so it defaults to SHOW_SIDE )
-hides User Table (nothing was passed so it defaults to SHOW_USER_LIST )

URL_KEY_SHOWDEPT
-shows all Dept's and all Users(active) and also shows dept select list (if SHOW_SELECT_DEPT is true)

-shows Users in support dept and also shows dept select list (if SHOW_SELECT_DEPT is true)

-shows Users by current users dept and also shows dept select list (if SHOW_SELECT_DEPT is true)

URL_KEY_SHOWSIDEBAR 
-shows side bar

-hides side bar

URL_KEY_SHOWHEADER 
-shows header

-hides header

URL_KEY_SHOWSIDEBAR 
-shows User Table

-hides User Table



Questions? 

Twitter: @SalesForceGirl  or Facebook


Friday, March 18, 2011

Global Helper Classes - Top 5 Methods to Include

Check for Null Value
Its always important to check for null, and since the check for null is the same over and over, it should be a Helper method. Since salesforce doesn't have a generic type variable that covers all like JS does with the type 'var' or C# does with the Type 'T'. Sure they have sObject, so we can do generic objects, but nothing that covers everything like strings, Lists etc. So in my Helper class I overloaded the method for each 'type' I need to check for null in. As you can see they are essentially the same method just checking a different type of variable.
/// <summary>
/// OVERLOADED
/// CHECKS IF STRING IS NULL OR EMPTY
/// </summary>
/// <param name="sInValueToCheck">STRING TO CHECK</param>
/// <returns>TRUE IF NOT NULL</returns>
public static boolean CheckForNotNull(string sInValueToCheck)
{
      if(sInValueToCheck != null && sInValueToCheck != '' && sInValueToCheck.toLowerCase() != 'null')
      {
            return true;
      }
      return false;
}
/// <summary>
/// OVERLOADED
/// CHECKS IF SOBJECT LIST IS NULL OR EMPTY
/// </summary>
/// <param name="lstInValueToCheck">STRING TO CHECK</param>
/// <returns>TRUE IF NOT NULL</returns>
public static boolean CheckForNotNull(List<sobject> lstInValueToCheck)
{
      if(lstInValueToCheck != null && lstInValueToCheck.size() > 0)
      {
            return true;
      }
      return false;
}

Get Param
I cant tell you how many times i need to get a param from the page, so it just made scene to include it here. As you should all know, when using params you should always reduce the case to lower and trim the param to ensure that when you go to compare it to what the value should be, it is always able to be matched up. But, Salesforce Id's are case-sensitive, so I created two Methods, one to reduce the case to lower, and a normal one. I couldn't overload the method since each has the same signature, plus this makes it obvious which one is the toLower() one. Another method i have, but am not showing here, isUrlParamOrDefault(string sInUrlKey, string sInDefualt), this enables you to always return a value instead of null.... well except of course if the sInDefault isn't null.
/// <summary>
/// GETS THE PARAM VALUE BASED ON KEY FROM URL
/// </summary>
/// <param name="sInUrlKey">URL PARAM KEY</param>
/// <returns>PARAM VALUE TO LOWER</returns>
public static string UrlParamToLower(string sInUrlKey)
{
      if(GlobalHelper.CheckForNotNull(sInUrlKey))
      {
            String UrlParam = ApexPages.currentPage().getParameters().get(sInUrlKey);
            if(GlobalHelper.CheckForNotNull(UrlParam))
            {
                  UrlParam = UrlParam.toLowerCase().trim();
            }
            return UrlParam;
      }
      return null;
}

/// <summary>
/// OVERLOADED
/// GETS THE PARAM VALUE BASED ON KEY FROM URL
/// </summary>
/// <param name="sInUrlKey">URL PARAM KEY</param>
/// <returns>PARAM VALUE</returns>
public static string UrlParam(string sInUrlKey)
{
      if(GlobalHelper.CheckForNotNull(sInUrlKey))
      {
            return ApexPages.currentPage().getParameters().get(sInUrlKey);
      }
      return null;
}

Check Value
Its always important to not only check for null, but to also make sure its the value you expect. So with these methods your able to check to see if the value passed is the same as the string passed, or if it is in the List that was passed. 
/// <summary>
/// OVERLOADED
/// CHECK URL PARAM AGAINST LIST TO ENSURE IT IS WHAT IT SHOULD BE
/// </summary>
/// <param name="sInUrl">URL PARAM</param>
/// <param name="lstInValueToCompare">LIST TO COMPARE VALUE AGAINST</param>
/// <returns>TRUE IF CORRECT VALUE ELSE FALSE</returns>
public static boolean CheckValue(string sInUrl, List<string> lstInValueToCompare)
{
      if(GlobalHelper.CheckForNotNull(lstInValueToCompare))
      {
            for(string s : lstInValueToCompare)
            {
                  if(GlobalHelper.CheckForNotNull(sInUrl))
                  {
                        if(sInUrl.toLowerCase().trim() == s.toLowerCase().trim())
                        {
                              return true;
                        }
                  }
            }
      }
      return false;
}
/// <summary>
/// OVERLOADED
/// CHECK URL PARAM AGAINST STRING TO ENSURE IT IS WHAT IT SHOULD BE
/// </summary>
/// <param name="sInUrl">URL PARAM</param>
/// <param name="sInToCompare">VALUE TO COMPARE AGAINST</param>
/// <returns>TRUE IF CORRECT VALUE ELSE FALSE</returns>
public static boolean CheckValue(string sInUrl, string sInToCompare)
{
      if(GlobalHelper.CheckForNotNull(sInUrl))
      {
            if(sInUrl.toLowerCase().trim() == sInToCompare.toLowerCase().trim())
            {
                  return true;
            }
      }
      return false;
}

Comparison (de-dupe)
When creating lists, its important to de-dupe to ensure uniqueness(if thats what is called for in your list). What these methods do is to check the current value passed against the list you want to add it to, ensuring that the passed value inst in the list. Honestly this is used constantly in my code to ensure the list doesn't contain duplicate values.
/// <summary>
/// OVERLOADED
/// CHECKS IF STRING IS IN LIST
/// </summary>
/// <param name="sInCurrentValue">STRING TO CHECK</param>
/// <param name="lstInCollectionValue">LIST TO COMPARE VALUE TO</param>
/// <returns>TRUE IF IN LIST</returns>
public static boolean ContainsItem(String sInCurrentValue, List<String> lstInCollectionValue)
{
      if(GlobalHelper.CheckForNotNull(lstInCollectionValue))
      {
            for(String s: lstInCollectionValue)
            {
                  if(GlobalHelper.CheckForNotNull(sInCurrentValue))
                  {
                        if(sInCurrentValue.toLowerCase().trim() == s.toLowerCase().trim())
                        {
                              return true;
                        }
                  }
            }
      }
      return false;
}
/// <summary>
/// OVERLOADED
/// CHECKS IF SOBJECT IS IN LIST
/// </summary>
/// <param name="objInCurrent">SOBJECT TO CHECK</param>
/// <param name="lstInCollection">LIST TO COMPARE VALUE TO</param>
/// <returns>TRUE IF IN LIST</returns>
public static boolean ContainsItem(sobject objInCurrent, List<sobject> lstInCollection)
{
      if(GlobalHelper.CheckForNotNull(lstInCollection))
      {
            for(sobject s: lstInCollection)
            {
                  if(s != null && objInCurrent != null)
                  {     
                        string sId = s.Id;
                        string cId = objInCurrent.id;
                        if(GlobalHelper.CheckForNotNull(cId) && GlobalHelper.CheckForNotNull(sId))
                        {
                              if(sId.toLowerCase().trim() == cId.toLowerCase().trim())
                              {
                                    return true;
                              }
                        }
                  }
            }
      }
      return false;
}

Add items to list
The Comparison methods would be used in something like the following method, which would return the completed list of items, de-duped. These methods allow you to either create a List<String> of Id's, or a List<sObject>, but it ensures that the list will be 'cleaned out' since it is using the Helper method of 'ContainsItem'
/// <summary>
/// OVERLOADED
/// ADDS SOBJECT IDS TO STRING LIST AFTER DEDUPING
/// </summary>
/// <param name="lstInSobjectValue">LIST TO GET VALUES FROM</param>
/// <returns>STRING LIST OF SOBJECT IDS</returns>
public static List<String> AddItemsToIdList(List<sobject> lstInSobjectValue)
{
      List<String> idList = new List<string>();
      if(GlobalHelper.CheckForNotNull(lstInSobjectValue))
      {
            for(sobject obj: lstInSobjectValue)
            {
                  if(obj != null)
                  {
                        if(!GlobalHelper.ContainsItem(obj.id, idList))
                        {
                              idList.add(obj.Id);
                        }
                  }
            }
            return idList;
      }
      return null;
}
/// <summary>
/// OVERLOADED
/// ADDS SOBJECT TO LIST AFTER DEDUPING
/// </summary>
/// <param name="lstInsObjectItems">LIST TO GET VALUES FROM</param>
/// <returns>SOBJECT LIST</returns>
public static List<sObject> AddItemsToList(List<sObject> lstInsObjectItems)
{
      List<sObject> newList = new List<sObject>();
      if(GlobalHelper.CheckForNotNull(lstInsObjectItems))
      {
            for(sObject con : lstInsObjectItems)
            {     
                  if(con != null)
                  {
                        if(!GlobalHelper.ContainsItem(con, newList))
                        {
                              newList.Add(con);
                        }
                  }
            }
            return newList;         
      }
      return null;
}

Questions? 
Twitter: @SalesForceGirl  or Facebook