Showing posts with label lambda. Show all posts
Showing posts with label lambda. Show all posts

Saturday, September 24, 2016

Generic Comparer for DistinctBy Using Func and IComparer For IEnumerable

During development of many projects, I have been come across to develop IComparer for different reasons and situations. Each time creating new comparer for different classes. Therefore, I have found solution to use single generic Comparer for all the IEnumerable extension which requires IComparer for the class.

I have used DistinctBy Example to go throw the Generic Extension Method.



 public static class Compare
    {
        public static IEnumerable<T> DistinctBy<T, TIdentity>(this IEnumerable<T> source, Func<T, TIdentity> identitySelector)
        {
            return source.Distinct(Compare.By(identitySelector));
        }

        public static IEqualityComparer<TSource> By<TSource, TIdentity>(Func<TSource, TIdentity> identitySelector)
        {
            return new DelegateComparer<TSource, TIdentity>(identitySelector);
        }

        private class DelegateComparer<T, TIdentity> : IEqualityComparer<T>
        {
            private readonly Func<T, TIdentity> identitySelector;

            public DelegateComparer(Func<T, TIdentity> identitySelector)
            {
                this.identitySelector = identitySelector;
            }

            public bool Equals(T x, T y)
            {
                return Equals(identitySelector(x), identitySelector(y));
            }

            public int GetHashCode(T obj)
            {
                return identitySelector(obj).GetHashCode();
            }
        }
    }

DistinctBy Extension Method


The function simply creates an extension method for IEnumerable together with Func with input of Type and outputting property of type TIdentity.

Using this way any class can provide the property that needs to be used for comparing and calculating hashcode for that property. However, TIdentity usually will be the unique key for the class object.

IEqualityComparer

This function simply create and object of Comparer passing in the Func delegate. The resulting DelegateComparer will use the Func Delegate to fetch the unique property and compare the unique property and calculate the hashcode.

Usage


public class AClass
{
  public string AProperty {get; set;}
}

public IEnumerable<AClass> DistinctAClass(IEnumerable<AClass> aClassList)
{
    aClassList.DistinctBy(a => a.AProperty);
}


Hope this helps to create unnecessary classes for comparer.

Tuesday, September 20, 2016

Adding Attributes on option tag of dropdown list box through reflection and Lambda Expression

Using Html Helper and Lambda expression for creating dropdown list and applying attributes on options tag of dropdown list


Traditional way of doing it.

<select name="listbox" id="listbox">
    @foreach (var item in Model)
           {

                   <option value="@item.UserRoleId" data-name="@item.Name" data-class="@item.ClassName">
                      @item.UserRole 
                   </option>                  
           }
    </select>

Instead of iterating through and attaching attributes inside foreach look
We can do like below

@Html.DropDownList(x => x.ReportName, Model.AvailableReports, x => x.TextField, x => x.NameField, "-- Select --", 
new Dictionary<string, Expression<Func<ReportSummaryModel, object>>>{ {"data-name", x => x.ReportActualName}, {"data-class", x => x.ReportClassName}})

It will output the select list with option tag and attribute on each option tag

Output


var s = new string();
<select>
 <option>--Select--</option>
 <option value="1" data-name="NameA" data-class="classA">A</option>                  
    <option value="2" data-name="NameB" data-class="classB">B</option>                  
    <option value="3" data-name="NameC" data-class="classC">C</option>                  
    <option value="4" data-name="NameD" data-class="classD">D</option>                  
</select>

Generic Tempelate
DropDownList<TPageModel, TSelectListModel, TProperty>
  • TPageModel : Model of Page
  • TSelectListModel : Model of Select List Item
  • TProperty: Will be object so can use any type
The parameters of custom dropdown list

public static MvcHtmlString DropDownList<TPageModel, TSelectListModel, TProperty>
(this HtmlHelper<TPageModel> htmlHelper, 
  Expression<Func<TSelectListModel, TProperty>> expression, IEnumerable<TSelectListModel> selectList, 
Expression<Func<TSelectListModel, TProperty>> textField, 
  Expression<Func<TSelectListModel, TProperty>> valueField, string optionLabel, IDictionary<string, 
Expression<Func<TSelectListModel, TProperty>>> optionsAttributes)


Parameters Descriptions
Expression<Func<TSelectListModel, TProperty>> expression
This is to provide the input property for which we are showing dropdown (Rename if want)


IEnumerable<TSelectListModel> selectList
List of class of which text and value field you will be using


Expression<Func<TSelectListModel, TProperty>> textField
Provide the text field property from the TSelectListModel


Expression<Func<TSelectListModel, TProperty>> valueField
Provide the value field property from the TSelectListModel


string optionLabel
Default Option Text for dropdown list box


IDictionary<string, Expression<Func<TSelectListModel, TProperty>>> optionsAttributes
This is to provide the attributes to be appear on option tag from the SelectListModel


public static MvcHtmlString DropDownList<TPageModel, TSelectListModel, TProperty>(this HtmlHelper<TPageModel> htmlHelper, Expression<Func<TSelectListModel, TProperty>> expression, IEnumerable<TSelectListModel> selectList, Expression<Func<TSelectListModel, TProperty>> textField, Expression<Func<TSelectListModel, TProperty>> valueField, string optionLabel, IDictionary<string, Expression<Func<TSelectListModel, TProperty>>> optionsAttributes)
        {
            string name = ExpressionHelper.GetExpressionText(expression);
            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            string defaultValue = string.Empty;
            ModelState modelState;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
            {
                if (modelState.Value != null)
                {
                    defaultValue = (string)modelState.Value.ConvertTo(typeof(string), null /* culture */);
                }
            }
            var listItemBuilder = new StringBuilder();
            // Make optionLabel the first item that gets rendered.
            if (optionLabel != null)
            {
                listItemBuilder.AppendLine(ListItemToOption(optionLabel, string.Empty, false, null));
            }
            /* Loop through each options
             * Convert each ListItem to an <option> tag */
            foreach (var listItem in selectList)
            {
                string text = listItem.GetType()
                                      .GetProperties()
                                      .Single(p => p.Name.Equals(ClassHelper.PropertyName(textField)))
                                      .GetValue(listItem, null)
                                      .ToString();
                string value = listItem.GetType()
                                       .GetProperties()
                                       .Single(p => p.Name.Equals(ClassHelper.PropertyName(valueField)))
                                       .GetValue(listItem, null)
                                       .ToString();
                bool isSelected = value.Equals(defaultValue);
                var htmlAttributes = new Dictionary<string, string>();
                foreach (var option in optionsAttributes)
                {
                    string propertyName = ClassHelper.PropertyName(option.Value);
                    htmlAttributes.Add(option.Key, listItem.GetType()
                                                             .GetProperties()
                                                             .Single(p => p.Name.Equals(propertyName))
                                                             .GetValue(listItem, null)
                                                             .ToString());
                }
                listItemBuilder.AppendLine(ListItemToOption(text, value, isSelected, htmlAttributes));
            }
            var tagBuilder = new TagBuilder("select")
            {
                InnerHtml = listItemBuilder.ToString()
            };
            tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
            return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal));
        }

Private method copied from mvc built in helper to generate option attribute

 internal static string ListItemToOption(string text, string value, bool isSelected, IDictionary<string, string> htmlAttributes)
        {
            var builder = new TagBuilder("option")
            {
                InnerHtml = HttpUtility.HtmlEncode(text)
            };
            if (value != null)
            {
                builder.Attributes["value"] = value;
            }
            if (isSelected)
            {
                builder.Attributes["selected"] = "selected";
            }
            if (htmlAttributes != null)
            {
                builder.MergeAttributes(htmlAttributes);
            }
            
            return builder.ToString(TagRenderMode.Normal);
        }


Utility function to get property name

public class ClassHelper
    {
        public static string PropertyName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
        {
            var body = expression.Body as MemberExpression;
            if (body == null)
            {
                body = ((UnaryExpression)expression.Body).Operand as MemberExpression;
            }
            return body.Member.Name;
        }
    }