Thursday, June 4, 2009

How to get the distinct of class object depend on the property?

In SQL illustrations (MSSQL 2005)

Query: SELECT * FROM spt_values;
Expected result: Getting the distinct field value on column name “type”
Solutions: SELECT DISTINCT(type) FROM spt_values;

Implementation on C# .NET

We can use LINQ that already supported since .NET 3.0. But how if we used .NET 2.0?
Let’s see below simple case.

  • Class clsHousing
  1:     /// <summary>
  2:     /// 
  3:     /// </summary>
  4:     public class clsHousing
  5:     {
  6:         private string id = String.Empty;
  7: 
  8:         /// <summary>
  9:         /// 
 10:         /// </summary>
 11:         public clsHousing(string id)
 12:         {
 13:             if (String.IsNullOrEmpty(id))
 14:                 throw new NullReferenceException();
 15: 
 16:             this.id = id;
 17:             this.houseDetails = new List<clsHouseDetail>();
 18:         }
 19: 
 20:         /// <summary>
 21:         /// 
 22:         /// </summary>
 23:         public clsHouseDetail[] HouseDetails
 24:         {
 25:             get { return houseDetails.ToArray(); }
 26:         }
 27:         private List<clsHouseDetail> houseDetails = null;
 28: 
 29:         /// <summary>
 30:         /// 
 31:         /// </summary>
 32:         /// <returns></returns>
 33:         public override string ToString()
 34:         {
 35:             return this.id;
 36:         }
 37: 
 38:         /// <summary>
 39:         /// 
 40:         /// </summary>
 41:         /// <param name="houseDetail"></param>
 42:         public void RegisterHouse(clsHouseDetail houseDetail)
 43:         {
 44:             this.houseDetails.Add(houseDetail);
 45:         }
 46: 
 47:         /// <summary>
 48:         /// 
 49:         /// </summary>
 50:         /// <param name="houseDetailID"></param>
 51:         public void UnregisterHouse(string houseDetailID)
 52:         {
 53:             //TODO: Remove from list
 54:         }
 55:     }


  • Class clsHouseDetail
  1:     /// <summary>
  2:     /// 
  3:     /// </summary>
  4:     public class clsHouseDetail
  5:     {
  6:         /// <summary>
  7:         /// 
  8:         /// </summary>
  9:         public clsHouseDetail()
 10:         { }
 11: 
 12:         /// <summary>
 13:         /// 
 14:         /// </summary>
 15:         public string OwnerID
 16:         {
 17:             get { return ownerID; }
 18:             set { ownerID = value; }
 19:         }
 20:         private string ownerID = String.Empty;
 21: 
 22:         /// <summary>
 23:         /// 
 24:         /// </summary>
 25:         public enHousingType Type
 26:         {
 27:             get { return type; }
 28:             set { type = value; }
 29:         }
 30:         private enHousingType type = enHousingType.A;
 31: 
 32:         /// <summary>
 33:         /// 
 34:         /// </summary>
 35:         /// <returns></returns>
 36:         public override string ToString()
 37:         {
 38:             return (this.OwnerID + " - " + this.Type.ToString());
 39:         }
 40:     }


  • Enumeration enHousingType
  1:     /// <summary>
  2:     /// 
  3:     /// </summary>
  4:     public enum enHousingType { A = 1, B = 2, C = 3, D = 4, E = 5 }


  • Class clsHousingManager
  1:     /// <summary>
  2:     /// 
  3:     /// </summary>
  4:     public class clsHousingManager
  5:     {
  6:         /// <summary>
  7:         /// 
  8:         /// </summary>
  9:         public clsHousingManager()
 10:         {
 11:             this.housing = new List<clsHousing>();
 12:             this.FillSampleValue();
 13:         }
 14: 
 15:         /// <summary>
 16:         /// 
 17:         /// </summary>
 18:         public clsHousing[] Housing
 19:         {
 20:             get { return housing.ToArray(); }
 21:         }
 22:         private List<clsHousing> housing = null;
 23:         
 24:         /// <summary>
 25:         /// 
 26:         /// </summary>
 27:         private void FillSampleValue()
 28:         {
 29:             clsHousing housing = null;
 30:             clsHouseDetail houseDetail = null;
 31:             
 32:             //Housing-1
 33:             housing = new clsHousing("Housing-1");
 34:             //  detail-1
 35:             houseDetail = new clsHouseDetail();
 36:             houseDetail.OwnerID = "Family-1";
 37:             houseDetail.Type = enHousingType.A;
 38:             housing.RegisterHouse(houseDetail);
 39: 
 40:             //  detail-2
 41:             houseDetail = new clsHouseDetail();
 42:             houseDetail.OwnerID = "Family-2";
 43:             houseDetail.Type = enHousingType.B;
 44:             housing.RegisterHouse(houseDetail);
 45: 
 46:             //  detail-3
 47:             houseDetail = new clsHouseDetail();
 48:             houseDetail.OwnerID = "Family-3";
 49:             houseDetail.Type = enHousingType.C;
 50:             housing.RegisterHouse(houseDetail);
 51: 
 52:             //  detail-4
 53:             houseDetail = new clsHouseDetail();
 54:             houseDetail.OwnerID = "Family-1";
 55:             houseDetail.Type = enHousingType.C;
 56:             housing.RegisterHouse(houseDetail);
 57: 
 58:             this.housing.Add(housing);
 59:         }
 60: 
 61:         /// <summary>
 62:         /// 
 63:         /// </summary>
 64:         public void Test()
 65:         {
 66:             clsHousing housing = (this.Housing.Length > 0 ? this.Housing[0] : null);
 67:             if (housing != null)
 68:             {
 69:                 //Test distinct of type    
 70:                 IEnumerable<enHousingType> iEnumHousingType = clsEnumerable.Select<clsHouseDetail, enHousingType>(
 71:                     housing.HouseDetails,
 72:                     delegate(clsHouseDetail houseDetail) { return houseDetail.Type; }
 73:                     );
 74:                 iEnumHousingType = clsEnumerable.Distinct<enHousingType>(iEnumHousingType);
 75:                 List<enHousingType> listHousingType = new List<enHousingType>(iEnumHousingType);
 76: 
 77:                 //Test distinct of owner-ID
 78:                 IEnumerable<string> iEnumOwnerID = clsEnumerable.Select<clsHouseDetail, string>(
 79:                     housing.HouseDetails,
 80:                     delegate(clsHouseDetail houseDetail) { return houseDetail.OwnerID; }
 81:                     );
 82:                 iEnumOwnerID = clsEnumerable.Distinct<string>(iEnumOwnerID);
 83:                 List<string> listOwnerID = new List<string>(iEnumOwnerID);
 84:             }
 85:         }
 86: 
 87:     }

How to get the distinct of property ownerID (string) or type (enHousingType) on class clsHouseDetail on collections?
For simple solutions, we can loop all collections, do checking, stored on temporary variable, and done.
I have other solutions for that.


Static class clsEnumerable

  1:     /// <summary> Delegate TResult.</summary>
  2:     /// <typeparam name="TSource"></typeparam>
  3:     /// <typeparam name="TResult"></typeparam>
  4:     /// <param name="arg"></param>
  5:     /// <returns></returns>
  6:     public delegate TResult Func<TSource, TResult>(TSource arg);
  7: 
  8:     /// <summary></summary>
  9:     /// <author>Stevanus Ronald</author><date>Wednesday, May 14, 2008</date>
 10:     public static class clsEnumerable
 11:     {
 12:         /// <summary> Select query object.</summary>
 13:         /// <author>Ronald</author><date>Wednesday, May 14, 2008</date>
 14:         /// <typeparam name="TSource"></typeparam>
 15:         /// <typeparam name="TResult"></typeparam>
 16:         /// <param name="source"></param>
 17:         /// <param name="selector"></param>
 18:         /// <returns></returns>
 19:         public static IEnumerable<TResult> Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)
 20:         {
 21:             if (source == null)
 22:                 throw new ArgumentNullException(source.GetType().ToString());
 23: 
 24:             if (selector == null)
 25:                 throw new ArgumentNullException(selector.GetType().ToString());
 26: 
 27:             return SelectIterator<TSource, TResult>(source, selector);
 28:         }
 29: 
 30:         /// <summary> Select iterator query object.</summary>
 31:         /// <author>Ronald</author><date>Wednesday, May 14, 2008</date>
 32:         /// <typeparam name="TSource"></typeparam>
 33:         /// <typeparam name="TResult"></typeparam>
 34:         /// <param name="source"></param>
 35:         /// <param name="selector"></param>
 36:         /// <returns></returns>
 37:         private static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)
 38:         {
 39:             foreach (TSource var in source)
 40:             {
 41:                 yield return selector(var);
 42:             }
 43:         }
 44: 
 45:         /// <summary> ToList iterator query object.</summary>
 46:         /// <author>Stevanus Ronald</author><date>Wednesday, May 14, 2008</date>
 47:         /// <typeparam name="TSource"></typeparam>
 48:         /// <param name="source"></param>
 49:         /// <returns></returns>
 50:         private static List<TSource> ToListIterator<TSource>(IEnumerable<TSource> source)
 51:         {
 52:             return new List<TSource>(source);
 53:         }
 54: 
 55:         /// <summary> Distinct query object.</summary>
 56:         /// <author>Stevanus Ronald</author><date>Wednesday, May 14, 2008</date>
 57:         /// <typeparam name="TSource"></typeparam>
 58:         /// <param name="source"></param>
 59:         /// <returns></returns>
 60:         public static IEnumerable<TSource> Distinct<TSource>(IEnumerable<TSource> source)
 61:         {
 62:             return Distinct<TSource>(source, null);
 63:         }
 64: 
 65:         /// <summary> Distinct query object.</summary>
 66:         /// <author>Stevanus Ronald</author><date>Tuesday, June 02, 2009</date>
 67:         /// <typeparam name="TSource"></typeparam>
 68:         /// <param name="source"></param>
 69:         /// <param name="comparer"></param>
 70:         /// <returns></returns>
 71:         public static IEnumerable<TSource> Distinct<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
 72:         {
 73:             if (source == null)
 74:                 throw new ArgumentNullException(source.GetType().ToString());
 75: 
 76:             return DistinctIterator<TSource>(source, comparer);
 77:         }
 78: 
 79:         /// <summary> Distinct iterator query object.</summary>
 80:         /// <author>Stevanus Ronald</author><date>Wednesday, May 14, 2008</date>
 81:         /// <typeparam name="TSource"></typeparam>
 82:         /// <param name="source"></param>
 83:         /// <param name="comparer"></param>
 84:         /// <returns></returns>
 85:         private static IEnumerable<TSource> DistinctIterator<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
 86:         {
 87:             Set<TSource> set = new Set<TSource>(comparer, ToListIterator<TSource>(source).Count);
 88: 
 89:             foreach (TSource var in source)
 90:                 if (!set.Contains(var))
 91:                 {
 92:                     set.Add(var);
 93:                     yield return var;
 94:                 }
 95:         }
 96: 
 97:     }
 98: 
 99:     /// <summary> Internal class Set.</summary>
100:     /// <author>Stevanus Ronald</author><date>Wednesday, May 14, 2008</date>
101:     /// <typeparam name="TElement"></typeparam>
102:     internal class Set<TElement>
103:     {
104:         private Int32[] buckets;
105:         private IEqualityComparer<TElement> comparer;
106:         private Int32 count;
107:         private Int32 freeList;
108:         private Slot[] slots;
109: 
110:         /// <summary>
111:         /// 
112:         /// </summary>
113:         /// <param name="count"></param>
114:         public Set(int count)
115:             : this(null, count)
116:         { }
117: 
118:         /// <summary>
119:         /// 
120:         /// </summary>
121:         /// <param name="comparer"></param>
122:         /// <param name="count"></param>
123:         public Set(IEqualityComparer<TElement> comparer, int count)
124:         {
125:             if (comparer == null)
126:                 comparer = EqualityComparer<TElement>.Default;
127: 
128:             this.comparer = comparer;
129:             this.buckets = new Int32[count];
130:             this.slots = new Slot[count];
131:             this.freeList = -1;
132:         }
133: 
134:         /// <summary>
135:         /// 
136:         /// </summary>
137:         /// <param name="value"></param>
138:         /// <returns></returns>
139:         public bool Add(TElement value)
140:         {
141:             return !this.Find(value, true);
142:         }
143: 
144:         /// <summary>
145:         /// 
146:         /// </summary>
147:         /// <param name="value"></param>
148:         /// <returns></returns>
149:         public bool Contains(TElement value)
150:         {
151:             return this.Find(value, false);
152:         }
153: 
154:         /// <summary>
155:         /// 
156:         /// </summary>
157:         /// <param name="value"></param>
158:         /// <param name="add"></param>
159:         /// <returns></returns>
160:         private bool Find(TElement value, bool add)
161:         {
162:             int hashCode = this.comparer.GetHashCode(value);
163:             for (int i = 0; i < this.slots.Length ; i++)
164:             {
165:                 if ((this.slots[i].hashCode == hashCode) && this.comparer.Equals(this.slots[i].value, value))
166:                 {
167:                     return true;
168:                 }
169:             }
170: 
171:             if (add)
172:             {
173:                 Int32 freeList;
174:                 if (this.freeList > 0)
175:                 {
176:                     freeList = this.freeList;
177:                     this.freeList = this.slots[freeList].next;
178:                 }
179:                 else
180:                 {
181:                     if (this.count == this.slots.Length)
182:                     {
183:                         this.Resize();
184:                     }
185:                     freeList = this.count;
186:                     this.count++;
187:                 }
188:                 Int32 index = this.buckets.Length - 1;
189:                 this.slots[freeList].hashCode = hashCode;
190:                 this.slots[freeList].value = value;
191:                 this.slots[freeList].next = this.buckets[index] - 1;
192:                 this.buckets[index] = freeList + 1;
193: 
194:             }
195: 
196:             return false;
197:         }
198: 
199:         /// <summary>
200:         /// 
201:         /// </summary>
202:         /// <param name="value"></param>
203:         /// <returns></returns>
204:         public bool Remove(TElement value)
205:         {
206:             int hashCode = this.comparer.GetHashCode(value);
207:             int index = hashCode % this.buckets.Length;
208:             int num3 = -1;
209:             for (int i = this.buckets[index] - 1; i >= 0; i = this.slots[i].next)
210:             {
211:                 if ((this.slots[i].hashCode == hashCode) && this.comparer.Equals(this.slots[i].value, value))
212:                 {
213:                     if (num3 < 0)
214:                     {
215:                         this.buckets[index] = this.slots[i].next + 1;
216:                     }
217:                     else
218:                     {
219:                         this.slots[num3].next = this.slots[i].next;
220:                     }
221:                     this.slots[i].hashCode = -1;
222:                     this.slots[i].value = default(TElement);
223:                     this.slots[i].next = this.freeList;
224:                     this.freeList = i;
225:                     return true;
226:                 }
227:                 num3 = i;
228:             }
229:             return false;
230:         }
231: 
232:         /// <summary>
233:         /// 
234:         /// </summary>
235:         private void Resize()
236:         {
237:             int num = (this.count * 2) + 1;
238:             int[] numArray = new int[num];
239:             Slot[] destinationArray = new Slot[num];
240:             Array.Copy(this.slots, 0, destinationArray, 0, this.count);
241:             for (int i = 0; i < this.count; i++)
242:             {
243:                 int index = destinationArray[i].hashCode % num;
244:                 destinationArray[i].next = numArray[index] - 1;
245:                 numArray[index] = i + 1;
246:             }
247:             this.buckets = numArray;
248:             this.slots = destinationArray;
249:         }
250: 
251:         /// <summary>
252:         /// 
253:         /// </summary>
254:         [StructLayout(LayoutKind.Sequential)]
255:         internal struct Slot
256:         {
257:             internal Int32 hashCode;
258:             internal TElement value;
259:             internal Int32 next;
260:         }
261: 
262:     }

So to getting the distinct of ownerID:

  1: //Test distinct of type    
  2: IEnumerable<enHousingType> iEnumHousingType = clsEnumerable.Select<clsHouseDetail, enHousingType>(
  3:    housing.HouseDetails,
  4:    delegate(clsHouseDetail houseDetail) { return houseDetail.Type; }
  5:    );
  6: iEnumHousingType = clsEnumerable.Distinct<enHousingType>(iEnumHousingType);
  7: List<enHousingType> listHousingType = new List<enHousingType>(iEnumHousingType);

And to getting the distinct of type:

  1: //Test distinct of owner-ID
  2: IEnumerable<string> iEnumOwnerID = clsEnumerable.Select<clsHouseDetail, string>(
  3:   housing.HouseDetails,
  4:   delegate(clsHouseDetail houseDetail) { return houseDetail.OwnerID; }
  5:   );
  6: iEnumOwnerID = clsEnumerable.Distinct<string>(iEnumOwnerID);
  7: List<string> listOwnerID = new List<string>(iEnumOwnerID);

Have fun!

No comments:

Post a Comment