Back to Basics: Moving beyond for, if and switch
I visit a lot of customers and look at a lot of code. I also worked with a number of large production code bases in my previous jobs and I see a lot of ifs, fors and switches. I see loops inside of loops with ifs inside them, all doing various transformations of data from one form to another. I see strings getting parsed to pull bits of data out in ways that are easy to say in English but take 100 lines to say in code.
Should they? When we are just getting started programming we learn about if first, then for, then the much abused switch statement.
I saw this little snippet on Miguel's blog a few weeks ago:
var biggerThan10 = new List;
for (int i = 0; i < array.Length; i++){
if (array [i] > 10)
biggerThan10.Add (array[i]);
}
It's straightforward. Take an array of ints and make a new list with those that are larger than 10. We've all see code like this a million times. Here's the same thing in a few other languages.
C#
var a = from x in array where x > 10 select x;
var b = array.Where(x => x > 10);
Ruby
a = array.select{|x| x >10}
JavaScript
a = array.filter(function(x){return x > 10});
I'd much rather write these one line operations than the loop and if above. I still see this out in the world, so perhaps people haven't seen enough examples. I asked friends on Twitter to submit their examples. Thank you Twitter friends!
Here's a few nice examples. Iron Shay has some nice LINQ examples on his blog. Please do share yours in the comments. Be sure to use <pre> tags.
NOTE: This is NOT about "shoving stuff into one line" but rather looking at solutions that are equally as readable but also simpler, terser, and less error prone than loops of loops.
def calculate_primes(n):
no_primes = []
primes = []
for i in range(2, 8):
for j in range(i*2, n, i):
no_primes.append(j)
for x in range(2, n):
if x not in no_primes:
primes.append(x)
return primes
calculate_primes(500)
# Can be like this instead!
(lambda n: [x for x in range(2, n) if x not in [j for i in range(2, 8) for j in range(i*2, n, i)]])(500)
From Aaron Bassett
foreach (var i in categories) {
foreach (var x in GetAllChildCategories(i.Id)) {
yield return x;
}
}
//Can be...
return categories.SelectMany(i => this.GetAllChildCategoriesIds(i.Id));
From James Hull
var inputNumbersInString = Console.ReadLine();
var inputNumbersStringArray = inputNumbersInString.Split(' ');
var inputNumbers = new List<int>();
for (int i = 0; i < inputNumbersStringArray.Length; ++i) {
inputNumbers.Add(int.Parse(inputNumbersStringArray[i]));
}
int maxNumber = inputNumbers[0];
for (int i = 1; i < inputNumbers.Count; ++i)
if (inputNumbers[i] > maxNumber)
maxNumber = inputNumbers[i];
Console.WriteLine(maxNumber);
//Or rather...
Console.WriteLine(Console.ReadLine().Split(' ').Select(t => int.Parse(t)).ToList().Max());
From Amit Saraswat
// create a poker deck as a list of two characters strings:
// rank, suite
char[] figures = "23456789TJQKA".ToCharArray();
char[] suites = "SHDC".ToCharArray();
List<string> deck = new List<string>();
foreach (var figure in figures) {
foreach (var suite in suites) {
deck.Add(string.Format("{0}{1}", figure, suite));
}
}
//Or, neatly
var cards = from r in "23456789TJQKA" from s in "SHDC" select "" + r + s;
From Jack Nova
bool include = false;
if (op == Operator.And) {
bool current = true;
foreach (var item in Items) {
current = current & item.Process();
}
include = current;
}
else {
bool current = false;
foreach (var item in Items) {
current = current | item.Process();
}
include = current;
}
return include;
//Or this lovely Aggregate
return op == Operator.And ?
Items.Aggregate(true, (current, item) => current & item.Process()) :
Items.Aggregate(false, (current, item) => current | item.Process());
From Kevin Meiresonne
sbyte[] sByteArray = new sbyte[100];
byte[] uByteArray = new byte[sByteArray.Length];
for (int i = 0; i < sByteArray.Length; i++) {
uByteArray[i] = (byte)sByteArray[i];
}
//Or, instead of the loop above
byte[] uByteArray1 = Array.ConvertAll(sByteArray, x => (byte)x);
From Fahad Mustafa
Scott: I have to say here that I prefer the first option. ;)
// This is the "classic" solution to the FizzBuzz problem.
for (int i = 1; i <= 100; i++) {
if (i % 3 == 0 && i % 5 == 0) {
Console.WriteLine("FizzBuzz");
}
else if (i % 3 == 0) {
Console.WriteLine("Fizz");
}
else if (i % 5 == 0) {
Console.WriteLine("Buzz");
}
else {
Console.WriteLine(i.ToString());
}
}
// One line
Enumerable.Range(1, 100).ToList().ForEach(n => Console.WriteLine((n % 3 == 0) ? (n % 5 == 0) ? "FizzBuzz" : "Fizz" : (n % 5 == 0) ? "Buzz" : n.ToString()));
From Craig Phillips
A good one...I'm surprised more people don't use this.
var temp = String.Empty;
foreach (var entry in myStringList) {
if (String.IsNullOrEmpty(temp)) {
temp = entry;
}
else {
entry += ", " + entry;
}
}
//becomes
var temp = String.Join(", ", myStringList)
From Holger Adam
A class with properties in one line of F#. That'd be a dozen or more lines of C#.
type Person = { Name:string; Age:int }
From Phillip Trelford
/// Input is a string with numbers : 10+20+30+40
/// Output is integer with required sum (100)
string input = "10+20+30+40";
var result = Regex.Split(input, @"\D+").Select(t => int.Parse(t)).Sum();
Console.WriteLine("Result is {0}" ,result);
From Srinivas Iyengar
There are a million things available to the programmer beyond the first three keywords we learn. What are your favorite patterns (doesn't matter what language) that have helped you break away from the basics and move to the next level?
Sponsor: Big thanks to the folks at DevExpress for sponsoring this week's feed. Check out a free trial of CodeRush, one of my favorite products! Introducing CodeRush by DevExpress. The Visual Studio add-in that helps you create more reliable applications. Tools to build & maintain your code without getting in the way of your IDE.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
The end result is usually the same anyway (in the compiled code).
Try to remember that the true cost of code is calculated over the lifetime of the app, not just how much it cost the first person to write it.
I have seen so many pointlessly terse routines in my life and all just because the programmer wanted it on one line.
The second point is that, this is just a matter of preference to developers. You can make shorter codes but they may be also harder to read, follow, and debug. If I'd pick one, I pick the Miguel's code because it's easy to read and follow, and don't forget the importance of this very simple rule in software engineering.
Besides, some of the code snippets you provided above are using some library functions to make a shorter code, but in fact behind the scenes, the same stuff happen.
A sidenote is that, code translation for some of these scenarios will be difficult to other languages or even to older versions of the same language.
As of LINQ, I don't think the original intention of LINQ was to make codes shorter. It was designed to make the data querying process native in the language in a uniform and standard way.
As the bottom line, I'd say that writing a shorter code is good as long as it's readable and doesn't have a performance penalty otherwise it's more like showing off your bicep size to other guys at gym and nothing else ;-)
// this is just the type declaration
quicksort :: Ord a => [a] -> [a]
// this says that sorting an empty list yields an empty list
// it's useful for terminating the recursion, below.
quicksort [] = []
// this line *is* the QS algorithm: choose a pivot, then put the lesser
// values on one side and the greater (or equal) values on the other
// In this case we choose the first item in the list as the pivot
// finally, do the same thing recursively on each side of the pivot.
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
if (nrOfDays == 1)
return "day"
else
return "days"
one line:
return (nrOfDays == 1) ? "day" : "days";
-------------
bool setDefault = p.Objs.Count(x => x.IsDefault) <= 0;
better:
bool setDefault = !p.Objs.Any(x => x.IsDefault);
-------------
string[] allItems = { "item1", "item2", "item3" };
string[] databaseItems = { "item2" };
var data = allItems
.SelectMany(item => databaseItems , (item, dbItem) => new { item, dbItem })
.Where(x => !x.item.Equals(x.dbItem, StringComparison.InvariantCultureIgnoreCase))
.Select(x => x.item);
foreach (var v in data)
Console.WriteLine(v.ToString());
code from http://teusje.wordpress.com/2011/07/11/selectmany-in-linq-c/
---------------
recursive lambda expressions:
http://teusje.wordpress.com/2011/09/09/recursive-lambda-expressions-in-c/
Console.Write(String.Join("\n", Enumerable.Range(1, 100).Select(n =>(n % 3 == 0) ? (n % 5 == 0) ? "FizzBuzz" : "Fizz" : (n % 5 == 0) ? "Buzz" : n.ToString())));
[)amien
does rewriting code to one line make your code faster or slower or is it not worth talking about these nanoseconds?
On over half of the items, I think the first is much easier to read.
On many items, the first is far easier to debug.
This should trump, "Look at me! I am so 1337 for being able to use LINQ!"
protected static bool IsSomeBlaBla(IList<Record> records)
{
bool returnValue = false;
if (records != null)
{
int sundayCount = 0;
int mondayCount = 0;
int tuesdayCount = 0;
int wednesdayCount = 0;
int thursdayCount = 0;
int fridayCount = 0;
int saturdayCount = 0;
foreach (Record in records)
{
switch (record.DayOfWeek)
{
case DayOfWeek.Sunday:
sundayCount++;
break;
case DayOfWeek.Monday:
mondayCount++;
break;
case DayOfWeek.Tuesday:
tuesdayCount++;
break;
case DayOfWeek.Wednesday:
wednesdayCount++;
break;
case DayOfWeek.Thursday:
thursdayCount++;
break;
case DayOfWeek.Friday:
fridayCount++;
break;
case DayOfWeek.Saturday:
saturdayCount++;
break;
}
}
returnValue = (sundayCount == 1 &
mondayCount == 1 &
tuesdayCount == 1 &
wednesdayCount == 1 &
thursdayCount == 1 &
fridayCount == 1 &
saturdayCount == 1);
}
return returnValue;
}
and a nice result of refactoring
protected static bool IsSomeBlaBla(IList<Record> records)
{
var byDayCounts = (records ?? Enumerable.Empty<Record>())
.GroupBy(record => record.DayOfWeek)
.Select(byDays => byDays.Count())
.ToList();
return byDayCounts.Count == 7 && byDayCounts.All(count => count == 1);
}
I say if you find it harder to read the one liners, practice reading more one liners or using the underlying methods/syntax.
There is obviously a limit though. Maybe if it goes past your IDE's horizontal space, it's too much :)
Also think of this more as exploration/learning/seeing how to do things differently. Scott's not saying to go back and rewrite code or to try to reduce code as much as possible all the time (!!!!!!!1111oneoneone) .. just expand your horizons
var array = Enumerable.Range(0, 1000).ToArray();
// approach 1
int c = 0;
for (int i = 0; i < array.Length; i++)
{
if (array[i] > 10)
c++;
}
// approach 2
c = array.Count(x => x > 10);
Running 1,000,000 iterations on these two approaches (on Release x86 build):
Scenario 1 (for/if): 685ms
Scenario 2 (linq): 8067ms
Please, plase do not advocate for using Linq to produce shorter, nicer to read etc. code unless it is accompanied by warning that it affects performance - as seen on the example above, it could even be by factor of 10.
http://msdn.microsoft.com/en-us/library/bwabdf9z.aspx
I really don't think it's a good idea to put a shorter version as the goal. I'd go with considering performance and readability, and then try to make it shorter if I can.
But you run a short piece of CPU-bound code million times even more, like in a web app.
Here's an equivalent Linq implementation:
var biggerThan10 =
array
.Select((x, i) => new { x, i })
.Where(_ => _.x > 10)
.Select(_ => _.i);
"This is [not] about "shoving stuff into one line" but rather looking at solutions that are equally as readable but also simpler, terser, and less error prone than loops of loops."
Ernest Hemingway elevated terse to an art form through the medium of the English language. Clearly, there is much to be gained by aspiring to a similar standard within the realm of programming languages.
Be that as it may, I can't speak for Scott, but I suspect he would be the last person that would argue for brevity at the expense of readable, maintainable code. At the end of the day, we're all just trying to code something that works and that doesn't inflict too much pain on the poor sap who has to maintain our code base after we're gone.
var temp = String.Join(", ", myStringList.ToArray())
becomes
var temp = String.Join(", ", myStringList)
Aside: Why use String and not string? Under some circumstances, using the C# name can save another line of code by not needing the 'using System;' namespace decl.
Rich - nice! Fixed.
Performance - There is time and a balance to be found when coding. I'm not advocating that we blindly throw out either. Code smartly and know what your choices are. The folks that are writing for,for,for,if,if that we are trying to educate are likely not making performant choices regardless. Baby steps.
from n in Enumerable.Range(1, 100)
let isFizz = n % 3 == 0
let isBuzz = n % 5 == 0
select isFizz && isBuzz ? "FizzBuzz" : isFizz ? "Fizz" : isBuzz ? "Buzz" : n.ToString()
)
.ToList()
.ForEach(Console.WriteLine);
I feel like this reads well if you substitute the words for the characters:
? = "if"
: = "else"
(or you could do the Console.WriteLine(String.Join()) trick.)
Also note, the String.Join above does not need the ToArray() in 4.0 - there's an overload that takes IEnumerable
could be
Console.WriteLine(Console.ReadLine().Split(' ').Select(int.Parse).ToList().Max());
Console.WriteLine(Console.ReadLine().Split(' ').Select(int.Parse).Max());
/// <summary>
/// An implementation of the maybe monad.
/// </summary>
/// <typeparam name="T"></typeparam>
public class Maybe<T>
{
/// <summary>
/// Encapsulates a value.
/// </summary>
public class Just : Maybe<T>
{
public T Value { get; private set; }
public override string ToString()
{
return Value.ToString();
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as Just;
if (other != null) return other.Value.Equals(this.Value);
return false;
}
public Just(T value)
{
this.Value = value;
}
}
/// <summary>
/// Represents an empty result.
/// </summary>
public class None : Maybe<T>
{
public static readonly Maybe<T> Instance = new None();
public override bool Equals(object obj)
{
return obj is None;
}
public override int GetHashCode()
{
return 0;
}
public override string ToString()
{
return "None";
}
private None()
{
}
}
}
/// <summary>
/// Provides factory and extension methods for the generic maybe class.
/// </summary>
public static class Maybe
{
/// <summary>
/// Creates a just value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static Maybe<T> Just<T>(T value)
{
return new Maybe<T>.Just(value);
}
/// <summary>
/// Creates an instance of None.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Maybe<T> None<T>()
{
return Maybe<T>.None.Instance;
}
/// <summary>
/// This is to allow the LINQ syntax "from x in m select f(x)"
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="x"></param>
/// <param name="selector"></param>
/// <returns></returns>
public static Maybe<TResult> Select<TValue, TResult>(this Maybe<TValue> x, Func<TValue, TResult> selector)
{
var v = x as Maybe<TValue>.Just;
if (v == null) return Maybe.None<TResult>();
return Maybe.Just(selector(v.Value));
}
/// <summary>
/// This is to allow the LINQ syntax "from x in m from y in n select f(x, y)"
/// </summary>
/// <typeparam name="TValueA"></typeparam>
/// <typeparam name="TValueB"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="selector"></param>
/// <returns></returns>
public static Maybe<TResult> SelectMany<TValueA, TValueB, TResult>(this Maybe<TValueA> x,
Func<TValueA, Maybe<TValueB>> y,
Func<TValueA, TValueB, TResult> selector)
{
var vx = x as Maybe<TValueA>.Just;
if (vx == null) return Maybe.None<TResult>();
var vy = y(vx.Value) as Maybe<TValueB>.Just;
if (vy == null) return Maybe.None<TResult>();
return Maybe.Just(selector(vx.Value, vy.Value));
}
/// <summary>
/// If the predicate is satisfied, returs value, otherwise returns none.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static Maybe<T> Where<T>(this Maybe<T> value, Func<T, bool> predicate)
{
if (value.IsNone()) return value;
var v = value.GetValue();
if (predicate(v)) return value;
return Maybe.None<T>();
}
/// <summary>
/// Checks if the value is None
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="v"></param>
/// <returns></returns>
public static bool IsNone<T>(this Maybe<T> v)
{
return v is Maybe<T>.None;
}
/// <summary>
/// Gets the inner value.
/// Returns "default(T)" if Maybe is None.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="v"></param>
/// <returns></returns>
public static T GetValue<T>(this Maybe<T> v)
{
var t = v as Maybe<T>.Just;
if (t != null) return t.Value;
return default(T);
}
}
This now allows me to write code without "IFs" (and avoid some ugly nested null checks). Here an example for solving quadratic equations from user given parameters:
static Maybe<Tuple<double, double>> Solve(Maybe<double> x, Maybe<double> y, Maybe<double> z)
{
var s = from a in x
from b in y
from c in z
let det = b * b - 4 * a * c
where det >= 0
select Tuple.Create((-b + Math.Sqrt(det)) / (2 * a), (-b - Math.Sqrt(det)) / (2 * a));
return s;
}
static Maybe<double> ParseDouble(string s)
{
double v;
if (double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out v)) return Maybe.Just(v);
return Maybe.None<double>();
}
var a = ParseDouble(paramAText);
var b = ParseDouble(paramBText);
var c = ParseDouble(paramCText);
Solve(a, b, c);
grsLSData.Open "SHAPE APPEND New adInteger AS LSID, New adVarChar(20) AS Name_LS, " & _
"SUM(RowData.RowSeatsFilled) AS LSSeatsFilled, SUM(RowData.RowSeatsCount) AS LSSeatsCount," & _
"((SHAPE APPEND New adInteger AS LSID, New adInteger AS RowID, New adVarChar(10) AS Name_Row, " & _
"SUM(SeatData.EventsSeatFilled) AS RowSeatsFilled, SUM(SeatData.EventsSeatCount) AS RowSeatsCount, " & _
"((SHAPE APPEND New adInteger AS RowID, New adInteger AS SeatID, New adVarChar(10) AS Name_Seat, " & _
"SUM(EventSeatData.Filled) AS EventsSeatFilled, COUNT(EventSeatData.Name_EventCode) AS EventsSeatCount, " & _
"((SHAPE APPEND New adInteger AS SeatID, New adVarChar(10) AS Name_EventCode, " & _
"New adVarChar(2) AS Status, New adDouble AS Color, New adInteger AS Filled, New adInteger AS Custom) " & _
"RELATE SeatID to SeatID) AS EventSeatData) " & _
"RELATE RowID to RowID) AS SeatData) " & _
"RELATE LSID TO LSID) AS RowData ", , adOpenStatic, adLockOptimistic
It was pretty awesome to write such a complicated class in a single line like that, unfortunately it was terribly slow. If it had good performance I wouldn't ever use anything else in VBA.
Of course, I much prefer using .NET over VBA after I had learned how to use .NET (am still learning of course).
Enumerable.Range(1, 100)
.Select(n => new { n = n, Fizzy = (n % 3 == 0), Buzzy = (n % 5 == 0) })
.Select(fb => new {n = fb.n, Fuzzy = (fb.Fizzy ? "Fizz" : "") + (fb.Buzzy ? "Buzz" : "")})
.Select(f => f.Fuzzy.Length > 0 ? f.Fuzzy : f.n.ToString())
Also if you add some simple type checking, LINQ comes out smelling like roses.
Take this LINQPad Query:
var enumerable = Enumerable.Range(0, 9999999);
var sw = new Stopwatch();
int c = 0;
// approach 1
sw.Start();
var array = enumerable.ToArray();
for (int i = 0; i < array.Length; i++)
{
if (array[i] > 10)
c++;
}
sw.Stop();
c.Dump();
sw.ElapsedMilliseconds.Dump();
// approach 2
sw.Restart();
c = enumerable.Count(x => x > 10);
sw.Stop();
c.Dump();
sw.ElapsedMilliseconds.Dump();
// approach 3
sw.Restart();
c = enumerable.AsParallel().Where(x => x > 10).Count();
sw.Stop();
c.Dump();
sw.ElapsedMilliseconds.Dump();
//Approach 4 - Type Checking?
var objectEnum = enumerable.OfType<object>().Concat(new[] { "Hello" });
sw.Start();
var objectArray = objectEnum.ToArray();
for (int i = 0; i < objectArray.Length; i++)
{
int outVal;
var isInt = int.TryParse(objectArray[i].ToString(),out outVal);
if (isInt && Convert.ToInt32(objectArray[i]) > 10)
c++;
}
sw.Stop();
c.Dump();
sw.ElapsedMilliseconds.Dump();
// approach 5
sw.Restart();
c = enumerable.OfType<int>().Count(x => x > 10);
sw.Stop();
c.Dump();
sw.ElapsedMilliseconds.Dump();
// approach 6
sw.Restart();
c = enumerable.AsParallel().OfType<int>().Where(x => x > 10).Count();
sw.Stop();
c.Dump();
sw.ElapsedMilliseconds.Dump();
As one poster said, most of them would have to be completely dismantled to really understand how they work, or even what they're supposed to do, esp when there's a bug in there and they aren't working properly.
Sometimes, more lines of code isn't a bad thing.
also Re-sharper does it in very nice way, it converts many for each loops to single line LINQ expressions,
but of course figuring out ourselves is much better :)
private Thing _something;
public Thing Something
{
get
{
if (_something == null)
{
_something = new Thing();
}
return _something;
}
}
With null coalescing:
private Thing _something;
public Thing Something { get { return _something ?? (_something = new Thing()); } }
Maybe a touch harder to grok at first, but when you have many of these in a class the saved vertical space makes the whole more digestible.
I'd like to answer this. In short, the single most important thing for me has been to understand how to use interfaces. Often, it's more readable, more maintainable and easier to write something with interfaces than with lots of switches/ifs.
It would be fun to scrape the first 3 words from each comment. Pretty funny. Two "Well,"'s, an "Unfortunately", a "Gotta say"...
Funny stuff.
public static bool ArraysEqual<T>(T[] firstArray, T[] secondArray)
{
// check length
if (firstArray.Length != second.secondArray)
{
return false;
}
// examine item by item
for (int i = 0; i < firstArray.Length; i++)
{
if (!Equals(firstArray[i], secondArray[i]))
{
return false;
}
}
return true;
}
Or use SequenceEqual() http://msdn.microsoft.com/en-us/library/bb348567.aspx
firstArray.SequenceEqual(secondArray));
var sb = new StringBuilder();
var stringArray = new []{"Test1","Test2","Test3"};
for(int i=0, count = stringArray.Length;i<count;i++){
if(i==count-1){
sb.Append(stringArray[i]);
}
else{
sb.Append(stringArray[i] + ",");
}
}
//Becomes
stringArray.Aggregate(String.Empty, (current, b) => current + ("," + b)).Substring(1);//Substring used to trim leading ','
Although i suspect the first implementation maybe more efficient....
Unfortunately the re-write is dependent on project deadlines. The available constructs is dependent on technology. Both of these could be answered if I could see the date the code was written right there in the IDE.
Obviously there is source control, but that's an extra step ;-)
http://www.aboutmycode.com/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-1/
private Dictionary<char, Func<Expression, Expression, Expression>> operations =
new Dictionary<char, Func<Expression, Expression, Expression>>
{
{ '+', (current, next) => Expression.Add(current, next) },
{ '-', (current, next) => Expression.Subtract(current, next) },
{ '*', (current, next) => Expression.Multiply(current, next) },
{ '/', (current, next) => Expression.Divide(current, next) }
};
public decimal Evaluate(string expression)
{
foreach (var operation in operations)
{
if (expression.Contains(operation.Key))
{
var parts = expression.Split(operation.Key);
Expression result = Expression.Constant(Evaluate(parts[0]));
result = parts.Skip(1).Aggregate(result,
(current, next) =>
operation.Value(current, Expression.Constant(Evaluate(next))));
var lambda = Expression.Lambda<Func<decimal>>(result);
var compiled = lambda.Compile();
return compiled();
}
}
decimal value = 0;
decimal.TryParse(expression, out value);
return value;
}
If Sitecore.Context.Item.Fields("FieldName") IsNot Nothing Then
MyProp = Sitecore.Context.Item.Fields("FieldName").Value
End If
So I made an extension method inspired by AttributeRouting:
public static TResult SafeGet<T, TResult>(this T o, Func<T, TResult> accessor) {
return o == null ? null : accessor.Invoke(o);
}
So now the above code becomes (in VB):
MyProp = Sitecore.Context.Items.Fields("FieldName").SafeGet(Function(x) x.Value)
I would have just used what AR had, but I wasn't sure try...catching would buy me much in this scenario.
Personally I prefer the lambda technique.
String.Join(", ", strings).TrimEnd(new char[]{',', ' '}) or something similar.
You missed a simple fix for approach #1 (see below). Just use a foreach loop to avoid converting into an array first. This is the fastest of them all. Also, for approach #4, you need to use Restart (not Start) and set c = 0 before the loop.
-Walt
// approach 7 (Added by Walt)
c = 0;
sw.Restart();
foreach (int i in enumerable) if (i > 10) c++;
sw.Stop();
sw.ElapsedMilliseconds.Dump("Foreach loop. Total = " + c);
Its a snippet of the code. Get pairwise two strings from a list with string[]
Original:
for (int col = 2; col < csv[i].Length; col += 2)
{
string outStart = csv[i][col];
string outEnd = csv[i][col + 1];
if (!string.IsNullOrEmpty(outStart) && !string.IsNullOrEmpty(outEnd))
{
wtg.OutagePeriods.Add(new Period()
{
From = DateTime.Parse(outStart),
To = DateTime.Parse(outEnd)
});
}
}
After with some smaller modification to the outer code:
var outagePeriods = (from start in input
from end in input
select new ScheduledMaintenance(start,end)).Skip(2);
in theory.
in real life it's impractical for 1 simple reason: debugger support is sorely lacking.
- you can't quickwatch lambdas. you can intellisense jQuery and you can repaint all the icons grey, but you still can't quickwatch lambdas... :(
- you can't meaningfully step through chained enumerables without dropping to disasm
- seriously, try debugging a 'group by', ugh!
my linq experience usually looks like this:
- write a long, complex linq query
- test it, it doesn't work
- try to debug it (cue expletives)
- re-write the whole query procedurally
- debug it
- fix it, easy!
- consider rewriting it as a linq query for neatness
- realize that this actually reduces maintainability since any significant changes to the query would require another double-rewrite loop probably introducing more bugs
- consider including the original linq query as a comment for documentation
- realize that this actually reduces maintainability since there's no way to ensure that the two implementations are, or remain equivalent
All times in the day with 15 minute intervals for a time picker...
var timesInDay =
from hour in Enumerable.Range(0, 24)
from minute in Enumerable.Range(0, 4)
select string.Format("{0:00}:{1:00}", hour, minute * 15));
var v = {Property1 = "Hello", Property2 = 32, PropertyThree = 'c'};
Unless I'm completely wrong (!)
The point I was trying to make though was that the type safety you can get easily and efficiently by using LINQ. For example:
// approach 8 (Added by Walt, Modified by Chris)
c = 0;
sw.Restart();
foreach (int i in objectEnum) if (i > 10) c++;
sw.Stop();
sw.ElapsedMilliseconds.Dump("Foreach loop. Total = " + c);
Throws an unhanded exception! If you were to throw in some type checking a la Approach 4 then your Approach 7 doesn't look so speedy.
Of course you don't see the real benefits when dealing with some of these 'simple' functional examples, but when you are writing generic functions in an OO world using LINQ in this manner really starts to become super effective and efficient.
It does appears there are at least two scenarios in the example rewrites. One scenarios is where a code block with mutli-line, perhaps nested, control flow is 'smushed' down to a single lines. Those are the ones that are using dot notation 5-10 levels deep on a single line. Please realize this is the about the same level of nesting and complexity, just potentially harder to read and debug.
The other scenario where the original coder missed, perhaps out of habit, use of native function. The example where the original code was replaced by usage of String.Join is a great example.
In a working development shop, I spend nearly no time on the first scenario... if code is accomplishing the job, settting out to compress it by line count is likely to introduce an argument over style, and even if not, doesn't add a ton of value. The second scenario is a great use of time, though, you could show the original code a new technique they weren't familiar with. Likewise, as a developer, I would appreciate a refactoring that i missed in a first pass at the code.
Love your work.
Mel
Not to nit-pick, but you've got the attribution wrong for the FizzBuzz snippet. It was myself that tweeted that!
https://twitter.com/#!/craigtptech/status/195404131945553920
And I'm now kinda embarassed to say that as I seem to have unleashed the "FizzBuzz Stairway to Heaven" judging by the number of comments offering a "better" solution! :)
List<string> fruits = new List<string>();
fruits.Add("apple");
fruits.Add("mango");
fruits.Add("grapes");
fruits.Add("oranges");
string CommaSeperated = string.Empty;
foreach (string s in fruits)
{
CommaSeperated += "," + s;
}
CommaSeperated = CommaSeperated.Substring(1, CommaSeperated.Length-1);
// With LINQ
CommaSeperated = fruits.Aggregate((a, b) => a + "," + b);
Eg. IEnumerable<int> GetNumbersBiggerThan10(int[] array)
{
//1 line or more lines doesn't matter : the name of the function will explain
}
public class Person {
public string Name { get; set; }
public int Age { get; set; }
}
In general, I prefer using the LINQ methods, as they are tried and tested. Additionally, as was pointed out in one of the videos by Microsoft when introducing LINQ, now you can read what the developer intended, rather than how he wants to do it, i.e. imperative v. declarative. You also gain lazy-evaluation and similar syntax when accessing other technologies (like XML or SQL).
If you have an issue with debugging, just add a few more intermittent variables. So it's not on one line? Who cares? It can be changed after you're done debugging (what I do), or you can leave it to the Compiler to optimize.
Console.WriteLine(Console.ReadLine().Split(' ').Select(t => int.Parse(t)).ToList().Max());
But I find it much easier to read:
Console.WriteLine
(
Console.ReadLine()
.Split(' ')
.Select(t => int.Parse(t))
.ToList()
.Max()
);
And I don't have a compiler handy, but while typing this out I thought perhaps it could be reduced to:
Console.WriteLine
(
Console.ReadLine()
.Split(' ')
.Select(int.Parse)
.Max()
);
my linq experience usually looks like this:
- write a long, complex linq query
- test it, it doesn't work
A solution in two easy parts:
1. Don't write long, complex queries.
2. Write unit tests.
--- Query ---
Select all employees from oData(ATOM) public web service http://services.odata.org/Northwind/Northwind.svc/ having job title starting from 'Sales', order result list by last name, print EmployeeId, LastName, FirstName, Title...
--- code snippet --
(from e in ((new DataServiceContext(new Uri(@"http://services.odata.org/Northwind/Northwind.svc/"))
.CreateQuery<dynamic>("Employees")).ToList())
where e.Title.StartsWith("Sales")
orderby e.LastName select e).ToList()
.ForEach(e => Console.WriteLine("{0}. {1} {2} - {3}", e.EmployeeID, e.LastName, e.FirstName, e.Title));
--- result ---
5. Buchanan Steven - Sales Manager
1. Davolio Nancy - Sales Representative
9. Dodsworth Anne - Sales Representative
7. King Robert - Sales Representative
3. Leverling Janet - Sales Representative
4. Peacock Margaret - Sales Representative
6. Suyama Michael - Sales Representative
---
Thank you.
else { temp += ", " + entry; }
and not
else { entry += ", " + entry; }
Right?
byte[] uByteArray1 = Array.ConvertAll(sByteArray, x => (byte)x);
use the following, which does exactly the same behind.
byte[] uByteArray1 = sByteArray.Cast<byte>().ToArray();
Comments are closed.
Fluent APIs are great at making linear code, but we *need* improvements in Visual Studio debugging with how it deals with fluent APIs: I should be able to put a breakpoint on any one of the calls. Today, you can only set it on the whole mess. Can you do something about it? :) Pretty please?