Practice Linq

 

This is my first learning & practice post and the technology I picked is Linq (Language Integreated Query).  First I downloaded the LINQPad from http://www.linqpad.net/; and get Northwind database from http://northwinddatabase.codeplex.com/. I tried to learn LINQPad, which is a nice tool to run not only LINQ, but also C# statements and expressions.

Then I downloaded 101 LINQ Samples from http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b for revision.

Revision Notes

  1. Several clauses in method syntax provides index value to use, such as Where, Select, TakeWhile and SkipWhile.  For example,
    (new [] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }).Where((n, index) => n > index).Select(n => n)
  2. SelectMany method: Projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence. For example,
    new [] {"donut", "muffin"}.SelectMany(element => element.ToCharArray())
  3. Reverse method: reversed from the order in the original source.
  4. Group by clause and for multiple columns grouping:
    new [] {1,2,3,4,5,6,7,8,9,10}.GroupBy(n=>n % 3).Select(g => g)
    from n in new [] {1,2,3,4,5,6,7,8,9,10}
    group n by n%3 into g
    select new { g.Key, g }
    group x by new { x.Column1, x.Column2 }
  5. Filter by object type:
    (new object[] { null, 1.0, "two", 3, "four", 5, "six", 7.0 }).OfType<double>()
  6. “Any” method is good to use for checking non-empty result instead of comparing count > 0; as “Any” method is just called next method from enumerable; therefore we don’t need to loop through the whole list.
  7. Be aware of query reuse, there is a good example in LINQ 101 Examples; which shows that you can run the same query with a simple filter before and after values changes.  And the result will return differently.

Q & A

  1. LINQ method and query expression, which should I use? which one is better?
    Quote from StackOverflow,

    Neither is better: they serve different needs. Query syntax comes into its own when you want to leverage multiple range variables. This happens in three situations: When using the let keyword, When you have multiple generators (from clauses), and When doing joins.

  2. How to debug Linq in Visaul Studio?
    Please refer to LINQ debugging in MSDN, and I found that you can set breakpoint in the predicate in VS2013 by moving the cursor to the predicate and hit F9.

Again, more hands-on practice is required to familiar with the LINQ syntax.  Work hard!

Sharing

Below are some queries I wrote for practice, you can copy and paste to LINQPad and run against Northwind database.

(from c in Customers where c.CustomerID.StartsWith("S") orderby c.ContactName select c).Skip(2).Take(3).Dump();

Customers.GroupBy(c => new { c.Country, c.PostalCode }).Select(g => g).Dump();

(from c in Customers group c by new { c.Country, c.PostalCode } into g select g).Dump();

(from c in Customers group c by new { c.Country, c.PostalCode } into g select new { g.Key, Total=g.Sum(c => c.Orders.Sum(o => o.OrderDetails.Sum(od => od.UnitPrice)))}).Dump();

(from c in Customers where c.CustomerID == "CACTU" select new { c.Country, c.PostalCode, s =c.Orders.Sum(o => o.OrderDetails.Sum(od => od.UnitPrice * od.Quantity))}).Dump();

(from c in Customers
join o in Orders on c.CustomerID equals o.CustomerID
join od in OrderDetails on o.OrderID equals od.OrderID
//where c.CustomerID == "CACTU"
group new {c, od } by new { c.Country, c.PostalCode } into g
select new {g.Key, s=g.Sum(r => r.od.UnitPrice * r.od.Quantity)}).Dump();
Advertisements

Find high interval value

Question
Given a set of entries, each containing a time index and a int count value, i.e.
class Entry
{
time:int
count:int
}

Write a function that will give the time interval with the highest count together, i.e.

if we had entries
100, 2
100, 1
110, 10
200, 4
1000, 3
1200, 8
and we ran something like int highestInterval( 50 ); it would return 100, because in 100-150, you have counts 2, 1, and 10.

Answer

    class Program
    {
        static void Main(string[] args)
        {
            var inputs = new[] {
                new [] {100, 2 },
                new [] {100, 1 },
                new [] {110, 10 },
                new [] {200, 4 },
                new [] {1000, 3 },
                new [] {1200, 8 }
            };

            int range = 50;
            int value = HighestInterval(inputs, range);
        }

        private static int HighestInterval(int[][] inputs, int range)
        {
            int start = 0;
            int end = 0;
            int total = 0;
            int maxIndex = 0;
            int maxTotal = 0;
            int size = inputs.GetLength(0);

            do
            {
                while (inputs[end][0] - inputs[start][0]  > range){
                    total-=inputs[start][1];
                    start++;
                }

                total += inputs[end][1];

                if (maxTotal < total){
                    maxIndex = start;
                    maxTotal = total;
                }

                end++;
            }while (end < size);

            return inputs[maxIndex][0];
        }
    }

SEO friendly URL characters

Recently I am building a web site for internet users; and our team needed to structure the site URL to ensure it is SEO friendly.  After search, I found that a Uniform Resource Identifier (URI): Generic Syntax document, http://www.ietf.org/rfc/rfc3986.txt published by The Internet Society. In page 12, it mentions that what reserved characters are; which should not be used in your site URL.

reserved = gen-delims / sub-delims

gen-delims = “:” / “/” / “?” / “#” / “[” / “]” / “@”

sub-delims = “!” / “$” / “&” / “‘” / “(” / “)” / “*” / “+” / “,” / “;” / “=”

From Google Webmaster, it is recommended to use dash (-) instead of underscore (_).

Find the largest prime numbers with a given number

Question:
Given a number, find the largest prime numbers less than or equal to it.

Answer:

function isPrimeNumber(value) {
  for (var i=2; i<=Math.ceil(Math.sqrt(value)); i++)
  {
    if (value % i === 0)
    {
      return false;
    }
  }

  return true;
}

function findLargestPrimeNumberLessThan(value) {
  if (isNaN(value)) {
    throw "Not a number."
  }

  var input = Math.floor(value);
  if (input <== 1) {
    throw "number should be greater than or equal to 2";
  }

  for (var i=input; i>=2; i--) {
    if (isPrimeNumber(i))
    {
      break;
    }
  }

  return i;
}

Find index of lookup value in an int array

Question:
Find minimum index of lookup value in an int array; which it may contain duplicate values, sorted in ascending order, and shifted randomly.  For example, this array [10,10,10,12,12,12,1,1,2,2,2,3,3,3,3,5,5,5,5,7,7,7,8,8,8,9] shifted 10-12 to the beginning.

Workable solution:


static void Main(string[] args)
        {
            var index = find(new[] { 10, 10, 10, 12, 12, 12, 1, 1, 2, 2, 2, 3, 3, 3, 3, 5, 5, 5, 5, 7, 7, 7, 8, 8, 8, 9, 10}, 9);
            Console.WriteLine(index);
        }

        static int find(int[] values, int lookup)
        {
            int start = 0;
            int end = values.Length - 1;
            int mid = 0;
            bool isFound = false;

            if (values[0] == lookup)
            {
                return 0;
            }

            while (start <= end && !isFound)
            {
                mid = start + (end - start) / 2;

                if (values[mid] > lookup)
                {
                    if (values[end] > lookup && values[mid] > values[end])
                    {
                         start = mid + 1;
                    }
                    else
                    {
                        end = mid - 1;
                    }
                }
                else if (values[mid] < lookup)
                {
                    if (lookup > values[start] && values[start] > values[mid])
                    {
                        end = mid - 1;
                    }
                    else
                    {
                        start = mid + 1;
                    }
                }
                else
                {
                    isFound = true;
                }
            }

            if (!isFound)
            {
                return -1;
            }

            end = mid;
            while (start < end)
            {
                mid = (start + end) / 2;

                if (values[mid] == lookup)
                {
                    end = mid;
                }
                else
                {
                    start = mid + 1;
                }
            }

            return (values[mid] == lookup ? mid: end);
        }