And110 - Lab 5 - LINQ Approach to Instructor Adaptor

ColinDabritzColinDabritz USUniversity ✭✭

In And110, Lab 5, you are implementing the ISectionIndexer to allow fast scrolling.

We discussed that LINQ might be a good approach. I was interested in it so I implemented the interface and mappings in LINQ after the class was over.

I did the queries in the constructor of the InstructorAdaptor, but you could use these same patterns to replace the provided SectionIndexBuilder methods, or even potentially "in line" in the implementations of GetPositionForSection, GetSectionForPosition, and GetSections (although it seems wasteful to run them over and over)

This is what I came up with:

public class InstructorAdapter : BaseAdapter<Instructor>, ISectionIndexer
{
    private readonly Activity context;
    private readonly List<Instructor> instructors;
    private readonly Java.Lang.Object[] sectionHeaders;

    private readonly Dictionary<int, int> sectionIndexByPosition;
    private readonly Dictionary<int, int> positionIndexBySectionIndex;

    // constructor sets up all data and mappings
    public InstructorAdapter(Activity context, List<Instructor> instructors)
    {
        this.context = context;
        this.instructors = instructors;

        // extract section headers by grouping instructors
        var headerLetters = (from instructor in instructors
                             group instructor by getHeader(instructor)
                                 into instructorSection
                                 orderby instructorSection.Key
                                 select instructorSection.Key).ToArray();

        // convert headers to Java objects for section headers
        sectionHeaders = (from letter in headerLetters
                          select (Java.Lang.Object)letter).ToArray();

        // assign positions to all instructors (by select with indexing)
        var instructorsWithPosition =
            instructors.Select((value, index) => new { InstructorPosition = index, InstructorData = value });

        // first grouping into basic sections with headers as keys
        var basicSections = from instructorWithPosition in instructorsWithPosition
                            group instructorWithPosition by getHeader(instructorWithPosition.InstructorData)
                                into instructorSection
                            orderby instructorSection.Key
                            select new { SectionHeader = instructorSection.Key, SectionInstructors = instructorSection };

        // index the sections (again using seelct with indexing)
        var indexedSections = basicSections.Select((value, index) =>
                            new { SectionIndex = index, value.SectionHeader, value.SectionInstructors }).ToList();

        // map the dictionary of section starting positions, from index as key to each sections first instructor position as value
        positionIndexBySectionIndex = indexedSections.ToDictionary(section => section.SectionIndex,
                                                                   section => section.SectionInstructors.First().InstructorPosition);

        // to go the other direction, we need to flatten the list into a simple mapping, including all instructor positions
        // (note this syntax is also a SelectMany)
        var instructorPositionSectionList = from section in indexedSections
                                            from instructor in section.SectionInstructors
                                            select new { instructor.InstructorPosition, section.SectionIndex };

        // map the dictionary of sections, from each instructor position as key to the containing section index as value
        sectionIndexByPosition = instructorPositionSectionList.ToDictionary(entry => entry.InstructorPosition,
            entry => entry.SectionIndex);
    }

    // gets a 'header' value (key) for an instructor
    //  Header = The first letter, capitalized
    private string getHeader(Instructor instructor)
    {
        if (instructor.Name.Length < 1)
        {
            return "-"; // no value
        }
        return instructor.Name[0].ToString().ToUpper(); // return first letter, capitalized
    }

    // [--- Other methods omitted ---]

    #region ISectionIndexer Methods

    // given the INDEX of a section (not the header key!) provide the position of the FIRST item in that section
    public int GetPositionForSection(int sectionIndex)
    {
        return positionIndexBySectionIndex[sectionIndex];
    }

    // given the position of an item, give the INDEX for that section (not the header key!)
    public int GetSectionForPosition(int position)
    {
        return sectionIndexByPosition[position];
    }

    // get all section header values as java objects
    public Java.Lang.Object[] GetSections()
    {
        return sectionHeaders;
    }
    #endregion
}

This seemed to work well. The LINQ statements could be compressed further, but I separated sections out for clarity where I could.

I also noticed that using the Android player with Nexus 10 (KitKat) 4.4.2, API 19
the fast scroll UI did NOT appear, unless I forced it by setting this in the view:

    instructorsList.FastScrollAlwaysVisible = true;

Did anyone else try a LINQ implementation? Thoughts?

Posts

Sign In or Register to comment.