Scott Hanselman

Back to Basics: Moving beyond for, if and switch

April 26, 2012 Comment on this post [72] Posted in Back to Basics
Sponsored By

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.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
April 27, 2012 0:09
There should be a metric using the average angle of your code. Non-angular (or linear as one might call it more simply) is easier to follow because yes, it's linear and we think linearly most of the time.
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?
April 27, 2012 0:10
Creating short and terse code is all well and good, and in some cases can help create more concise code. LINQ especially has helped with this. But I still believe the good ol' if and foreach constructs trumph it in readability, especially for nested operations.

The end result is usually the same anyway (in the compiled code).
April 27, 2012 0:12
Sure I'm all for that but sometimes putting a huge linq expression or some other thing chained together on one line is impossible to debug without rewriting it.

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.
April 27, 2012 0:14
Well, the truth is that conditional and unconditional branches are a huge part of machine language and one of the biggest impacts on the performance of the system. 30 years ago, it was like that, today it's like that, and 100 years from now, probably, it will be like that, so the first point is that in any way you write these codes, the compiled code will use the same ugly machine code!

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 ;-)
April 27, 2012 0:16
Well, there's the classic Haskell quicksort, which is not only concise but actually representative of the QS algorithm:

    
// 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
April 27, 2012 0:18
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/
April 27, 2012 0:24
That FizzBuzz in LINQ uses the non-existent ForEach method that people create - a better version is:

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
April 27, 2012 0:26
F#

let array = [| 5..15 |]
let a = Array.filter(fun x -> x > 10) array
April 27, 2012 0:26
I've got one little question:
does rewriting code to one line make your code faster or slower or is it not worth talking about these nanoseconds?
April 27, 2012 0:32
But what difference does it make in the actual complied code?I do think some of the nested loops are easier to read and as someone else mentioned easier to debug.I do take your point though, in some cases there's a huge difference in lines of code.
April 27, 2012 0:36
Chains are much harder to read. Maintainability trumps "cool" every time. As a manager, I am surprised that you wouldn't know this.

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!"
P R
April 27, 2012 0:38
Oh. Here is one "big" example:

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);
}
April 27, 2012 0:50
less lines of code = less chance of bugs.
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
April 27, 2012 0:50
Unfortunately LINQ has now created a whole generation of coders who completely ignores any perception of writing performant code. for/if are compiled into nice machine code, whereas .Where() creates instances of enumerator class and then iterates through that instance using MoveNext method...

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.
April 27, 2012 0:58
@Damien: ForEach() exists in List<T> in .NET 4.0:

http://msdn.microsoft.com/en-us/library/bwabdf9z.aspx
April 27, 2012 1:01
As a follow up to what Knaģis mentioned, yes, there is a chance that using these LINQ codes affect the performance in cases, and that's because LINQ uses a bunch of conditions and other stuff in its own logic, too, which may not be necessary for the code you're writing.

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.
April 27, 2012 1:07
Knaģis, in real apps, you don't run a million iterations of cpu-bound code (ok, excluding scientific apps)
April 27, 2012 1:09
@Diego Mijelshon

But you run a short piece of CPU-bound code million times even more, like in a web app.
April 27, 2012 1:14
There's a small mistake regarding the first code snippet: it takes the indexes of the numbers larger than 10, not the numbers themselves. So the following snippets are not equivalent to the first...

Here's an equivalent Linq implementation:


var biggerThan10 =
array
.Select((x, i) => new { x, i })
.Where(_ => _.x > 10)
.Select(_ => _.i);
April 27, 2012 1:46
I think Scott's point with this post was not that less code (as measured via line count) is always preferable. I think this because, oh look, that's what he says:
"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.
April 27, 2012 1:52
No need to use ToArray() with Net4

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.
April 27, 2012 2:14
David - Thanks for getting it. ;) Yes, the point is to move beyond the basics, it's not to be wacky and shove everything into a ridiculous LINQ one liner.

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.
April 27, 2012 2:21
While some of the chaining constructs do make code easier to read, the more complex ones should probably be left as loops as it breaks it down nicely for the reader. Take the example by Aaron Bassett, the first method clearly shows that it is calculating primes based on a calculated list of non-primes; whereas the one liner just looks like a massive condition.
April 27, 2012 2:36
(
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
April 27, 2012 2:46
Console.WriteLine(Console.ReadLine().Split(' ').Select(t => int.Parse(t)).ToList().Max());

could be

Console.WriteLine(Console.ReadLine().Split(' ').Select(int.Parse).ToList().Max());
Ben
April 27, 2012 2:47
In fact, ToList is also redundant:

Console.WriteLine(Console.ReadLine().Split(' ').Select(int.Parse).Max());
Ben
April 27, 2012 3:09
I love one liners but sometimes it's really hard to debug when you have a problem. Perhaps the vs team can do something to improve that experience, deconstructing linq to if else forach when debugging
April 27, 2012 3:10
This is a big one, but what I did was translate the Haskell type Maybe into C# and added some extension methods for LINQ support:
/// <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);

April 27, 2012 4:55
Here's a code I did in VBA experimenting with a shaped ADO on-the-fly class:

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).
April 27, 2012 5:06
I always hated the double-test of divisors for FizzBuzz hence:


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())
April 27, 2012 5:29
@david-sehnal.myopenid.com you've just convinced me that I finally need to learn Haskell.
April 27, 2012 5:51
Knaģis - You are converting an enumerable to an array and back - it comes out more even when you don't do this.

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();

April 27, 2012 5:51
Gotta say, I like the +ability+ to compress a complex expression down like you can with lambdas, Linq and fluent paradigms, but a lot of these examples are way too "look at this, this is cool!" for me to want to use in actual production code that others will have to read and deal with one day.

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.
April 27, 2012 9:25
very cool examples thanks!

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 :)
April 27, 2012 10:26
Before null coalescing:


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.
April 27, 2012 11:06
The discussion above has turned to pro/con of one-liners. The original question was "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?".

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.
April 27, 2012 11:10
Two things good sir... I believe you're missing some links and references in your Ruby and Javascript code here AND...

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.
April 27, 2012 11:22
LOL, yes Thank You Rob for Where, Select and Filter. ;)
April 27, 2012 11:58
Comparing Sequences

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));
April 27, 2012 12:07
CSV Generation

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....
April 27, 2012 13:11
The problem I find is when I review code like this I'd like to see the date it was written. It isn't always immediately obvious how to express a solution in a different manner. I quite often start out with the basics on a problem I don't know and then go back and re-write.

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 ;-)
April 27, 2012 13:18
If it's just for inline use, one can use anonymous types in C# where the class doesnt's even need to be implemented.
April 27, 2012 13:59
I have a blog post about building math expression evaluator in 32 lines of code using Linq:

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;
}
April 27, 2012 15:02
Your article is awesome and informative also.......thanks for such a nice article....:)
April 27, 2012 16:53
I was doing some Sitecore stuff (in VB :( and was doing this:

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.
April 27, 2012 20:46
Strenge that the 'join .. On .. Equals' doesn't show up, which has power that the nested loops can only dream of.
April 27, 2012 21:45
You realize that in most cases the shorter, one liner is usually just doing the same looping behind the scenes. So I'd say from a readability standpoint for someone that knows lambda's then, yes you're correct. However, any coder that's worth a damn can look at any loop within loop and can determine what's going on.

Personally I prefer the lambda technique.
April 27, 2012 22:42
The problem with the String.Join(", ", strings) suggestion is that the returned string has an extra ", " (comma, space) appended. Granted, you can always Trim() this and remove the extra comma, in line, as well.
String.Join(", ", strings).TrimEnd(new char[]{',', ' '}) or something similar.
April 27, 2012 23:04
Even John Carmack thinks functional is they way to go. link
April 28, 2012 0:16
Chris,

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);
April 28, 2012 2:05
Just got this from a newly made codereview.
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);
April 28, 2012 21:00
i have to agree. functional style is much better.

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
April 28, 2012 22:17
jimmyt53.myopenid.com, string.Join doesn't append an extra ", " at the end, that's what is so good about it.
April 29, 2012 0:32
This one is a rather cool LINQ hack I came up with for generating data rather than filtering. I saw the original developer's code which was literally half a page of a new List<string>() adding a new string for each time. It had two mistakes in it.

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));

April 29, 2012 22:36
The F# comment seems to miss something out. Anonymous types allow the same thing to be done in C# 4.0 like so:

var v = {Property1 = "Hello", Property2 = 32, PropertyThree = 'c'};

Unless I'm completely wrong (!)
April 30, 2012 4:13
Walt - Rushed code - missed a few things :) There are two .Where(x => x > 10).Count() which could be just .Count(x => x > 10) which is causing a double enumeration, which effects performance.

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.
April 30, 2012 9:27
FWIW, Generally, I find the multi-line code examples more readable, and when it comes down to brass tacks, more sustainable and maintainable over the long haul. You would have to show me a material performance improvement of the single line version vs. multi-line versions of most of these to get me on board.

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.
April 30, 2012 9:53
Just remember that the "Code" is actually human understandable words that is converted to the instructions to do what ever you want. The "Code" is a document and must be understandable by others that want to use or improve the application. Always write so that others can understand your document. I specially make use of lots of comments and simplified code to solve complexed problems.
April 30, 2012 10:49
Hi Scott, small typo in your note; I assmue you meant to say "This is *not* about shoveing things onto one line" etc.etc. Feel free to remove this post when you correct.

Love your work.

Mel
April 30, 2012 14:30
Hi Scott.

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! :)

April 30, 2012 22:01
nslig rtfortytwo - That will make an anonymous object while the F# line will actually define a new type (class) called Person.
April 30, 2012 22:16
Can have look here !!
       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);
May 01, 2012 18:06
For me, the question is not to put it in 1 line or not, but to put in a separate function.
Eg. IEnumerable<int> GetNumbersBiggerThan10(int[] array)
{
//1 line or more lines doesn't matter : the name of the function will explain
}

May 01, 2012 20:32
Lets do it!
May 02, 2012 13:02
Just wanted to point out that to make the same Person class in C# would only require 4 lines, not dozens:
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.
May 03, 2012 1:06
This (from the examples) is fine...

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()
);

May 03, 2012 1:09
Piers Haken wrote:
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.
May 04, 2012 13:45
Just kidding - the following coding style is good for "quick & dirty" solutions but questionable if used everywhere in one's programming codebase:

--- 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.
May 11, 2012 12:49
I'm pretty sure the Holger Adam example should read:

else { temp += ", " + entry; }


and not

else { entry += ", " + entry; }


Right?
November 22, 2012 16:34
Quite late, i know. But instead of this
byte[] uByteArray1 = Array.ConvertAll(sByteArray, x => (byte)x);


use the following, which does exactly the same behind.

byte[] uByteArray1 = sByteArray.Cast<byte>().ToArray();
April 29, 2013 11:02
Great Article. I face the same problem in my team work. Too many lines can be replaced with just one or two lines, if we know the latest Tech & know the tools and APIs we use.

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.