Click here to Skip to main content
Click here to Skip to main content

The Elegant Art of Programming

By , 18 May 2012
 

This is an alternative to the original poster's "solution 1", which really does need some help.

    public static bool
    AllTheSame<T>
    (                                 
      this System.Collections.Generic.IList<T> List
    )                                      
    where T : System.IEquatable<T>
    {                                                  
      if ( List == null )
      {
        throw ( new System.ArgumentNullException ( "List" , "List must not be null" ) ) ;
      }

      bool result = true ;

      if ( List.Count > 1 )
      {                                
        T first = List [ 0 ] ;    
                                
        if ( (object) first == null )
        {
          for ( int i = 1 ; result && ( i < List.Count ) ; i++ )
          {
            result = (object) List [ i ] == null ;
          }
        }
        else
        {
          for ( int i = 1 ; result && ( i < List.Count ) ; i++ )
          {                         
            result = first.Equals ( List [ i ] ) ;
          }
        }
      }
    
      return ( result ) ;
    }

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

PIEBALDconsult
Software Developer (Senior)
United States United States
Member
BSCS 1992 Wentworth Institute of Technology
 
Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.
 
OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB
 
---------------
 
"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]
 

"Typing is no substitute for thinking." -- R.W. Hamming
 
"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup
 
ZagNut’s Law: Arrogance is inversely proportional to ability.
 
"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon
 
"linq'ish" sounds like "inept" in German -- Andreas Gieriet
 

"Things would be different if I ran the zoo." -- Dr. Seuss
 
"Wrong is evil, and it must be defeated." – Jeff Ello
 
"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw
 

"Omit needless local variables." -- Strunk... had he taught programming
 
"DON'T BE LIBERAL IN WHAT YOU ACCEPT!"
 
"Software Engineers don't have Trophy Wives; they have Presentation Layers."
 
"We learn more from our mistakes than we do from getting it right the first time."
 
"I'm an old dog and I like old tricks."
 
"Sometimes the envelope pushes back and sometimes you get a really nasty paper cut."
 
"A method shall have one and only one return statement."
 
My first rule of debugging: "If you get a different error message, you're making progress."
 
My golden rule of database management: "Do not unto others' databases as you would not have done unto yours."
 
My general rule of software development: "Design should be top-down, but implementation should be bottom-up."
 
"Today's heresy is tomorrow's dogma."
or
"Today's dogma is yesterday's heresy."

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
SuggestionHow about a linq'ish approach?memberAndreas Gieriet18 May '12 - 4:46 
How about a linq'ish approach? BTW: "linq'ish" sounds like "inept" in German Wink | ;-)
 
// Checks if all values are equal by means of the default equality comparer for T.
// Returns true if empty, has only one element, or all are equal; returns false otherwise.
public static bool AllTheSame<T>(this IEnumerable<T> source)
{
    return AllTheSame<T>(source, EqualityComparer<T>.Default);
}
// Checks if all values are equal by means of the equality comparer.
// Returns true if empty, has only one element, or all are equal; returns false otherwise.
public static bool AllTheSame<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer)
{
    var it = source.GetEnumerator();
    if (it.MoveNext())
    {
        var first = it.Current;
        while (it.MoveNext()) if (!comparer.Equals(first, it.Current)) return false;
    }
    return true;
}
 
Cheers
Andi
GeneralRe: How about a linq'ish approach?memberPIEBALDconsult18 May '12 - 5:31 
You need to read the original post -- it shows Linq, and many others have posted various Linq alternatives.
AnswerRe: How about a linq'ish approach?mvpLuc Pattyn18 May '12 - 6:27 
I like that a lot. Thanks.
 
IMO there is no LINQ involved, it is pure generics.
 
Smile | :)
Luc Pattyn [My Articles] Nil Volentibus Arduum

GeneralRe: How about a linq'ish approach?memberAndreas Gieriet18 May '12 - 21:14 
Ok, I might have to elaborate why I say "linq'ish": It is an extension method that has a signature in the spirit of linq functions as found in the Enumerable namespace:
  • it is an extension method for IEnumerable<T>
  • it provides comparator parameter to let the client pass an approriate equality functionality
  • it provides an overload which defaults to the default comparer of the given generic type
That's why I consider it "in the spirit of Linq", respectively "linq'ish".
 
Using it:

original post (simplified)this suggestion
// take the default comparer for double
if (myList.Distinct().Count()>1)
{ ... }
// physically meaningful comparer
if (myList.Distinct(Pressure.Equality).Count()>1)
{ ... }
// take default comaparer for double
if (myList.AllTheSame())
{ ... }
// physically meaningful comparer
if (myList.AllTheSame(Pressure.Equality))
{ ... }
 
Even if I would leave away the word "linq'ish", I still consider putting the whole functionality into a function is my preferred way of doing this. I do that even for single liners to show the intent in a kind of self exlaining way.
 
I consider the use of Distinct() in the original post a bit an overshot since it calculates too many data.
 
There is a similar trap: if (SomeExtensivDataAquisition().Count()>0). This calculates all the data to simply check if there is any data at all. If the problem is such that there is most of the time some data, one has far better direct methods to do so: e.g. Contains(...) or FirstOrDefault(...). I hesitate to use constructs if (...Count() > 0) and alike in connection with IEnumerable<T> objects.
 
Cheers
Andi
GeneralRe: How about a linq'ish approach?memberPIEBALDconsult19 May '12 - 5:01 
Andreas Gieriet wrote:
I still consider putting the whole functionality into a function is my
preferred way of doing this

 
Totaly agree. It applies a descriptive name to a technique, makes it more easily re-usable, and more readily maintainable. I don't think the OP understands that.
 

Andreas Gieriet wrote:
a bit an overshot

 
Yes, very much. As well as dupicating data (even if it's just references).
AnswerRe: How about a linq'ish approach?mvpLuc Pattyn19 May '12 - 5:17 
Thanks. I see what you meant now. Still I don't even need to know about LINQ to appreciate your code, but yes it does fit in the LINQ way of doing things.
 
And I agree about the possible overkill by LINQ, when using Distinct, Count, and such when there is no real need for them.
 
Smile | :)
Luc Pattyn [My Articles] Nil Volentibus Arduum

GeneralRe: How about a linq'ish approach?memberPIEBALDconsult18 May '12 - 7:38 
I don't see that devolving what should be a foreach into its component GetEnumerator, MoveNext, and Current parts improves the "elegance" any. If it's simply so you don't break or return out of it, then I think you need to reevaluate your coding values* -- it's like replacing a goto with something that works like a goto simply so you don't have a goto.
 
At any rate, I chose to use IList so I could use an index and a for loop rather break out of a foreach. Sigh | :sigh:
 

* I'm not saying mine are better. I also understand that some times we post code that was whipped up quickly just to illustrate a point. Personally, I don't like multiple returns in a method and I prefer not to use break and continue if they can easily be avoided.
 

Andreas Gieriet wrote:
"linq'ish" sounds like "inept"

 
That about sums up how I feel about Linq. Thumbs Up | :thumbsup:
GeneralRe: How about a linq'ish approach?memberAndreas Gieriet19 May '12 - 2:41 
Well, I personally don't agree with my word game: "linq'ish" --> German "linkisch" ~ "inept"...
 
I find Linq and the respective C# functions (Where, Select, etc.) quite handy: you can chain them and they do late evaluation for iterating.
And I'm also a supporter for using iterators instead of indexed access whenever adequate. And I prefer many small functions over larger functions. And I employ idiomatic style where useful. And, and, and, ...
 
We both seem to prefer different coding styles.
 
Regrading "elegant": The title is badly chosen and misleading in my opinion - I would rather go for "expressive" or alike. What is elegant coding anyways (Straight to the point? Simple? ...)? I don't know. I have other expectations on coding style than being "elegant": correct, complete, robust, maintainable, documented, concise, expressive, not copy-paste, no code-bloat, universally usable, reasonable performant, "good-enough", little complexity per function...
 
The problem at hand is not a plain foreach problem; it is rather "get the first element (if there is any) and compare to the rest of the elements (if there are any)", so I chose to go the iterator way. See also alternatives to that further down in this comment.
 
To my suggestion: No, this function is programmed intentionally as it is. The changes I would agree to get the same level as linq functions: check the null source and allow for a null comparer (takes the default comparer of that type) - and maybe the formatting (adding blocks instead of the while-if-single-liner). The rest is ok for my standards.
 
So, I'm ok with the following code for production (place into an appropriate extension class):
  1  /// <summary>
  2  /// Checks if all values are equal by means of the equality comparer.
  3  /// </summary>
  4  /// <typeparam name="TSource">The type of the elements of source.</typeparam>
  5  /// <param name="source">The sequence to check for all equal elements.</param>
  6  /// <returns>True if empty, has only one element, or all are equal; false otherwise.</returns>
  7  /// <exception cref="ArgumentNullException">source is null</exception>
  8  public static bool AllTheSame<TSource>(this IEnumerable<TSource> source)
  9  {
 10      return AllTheSame<TSource>(source, null);
 11  }
 12  /// <summary>
 13  /// Checks if all values are equal by means of the given comparer.
 14  /// </summary>
 15  /// <typeparam name="TSource">The type of the elements of source.</typeparam>
 16  /// <param name="source">The sequence to check for all equal elements.</param>
 17  /// <param name="comparer">An IEqualityComparer<TSource> to compare values.</param>
 18  /// <returns>True if empty, has only one element, or all are equal; false otherwise.</returns>
 19  /// <exception cref="ArgumentNullException">source is null</exception>
 20  public static bool AllTheSame<TSource>(this IEnumerable<TSource> source,
 21                                         IEqualityComparer<TSource> comparer)
 22  {
 23      // Check preconditions and do default handling.
 24      if (source == null) throw new ArgumentNullException("Value cannot be null.", "source");
 25      var equalityComparer = comparer ?? EqualityComparer<TSource>.Default;
 26              
 27      // Check if any element is different to the first one and return out of the loop if so.
 28      var it = source.GetEnumerator();
 29      if (it.MoveNext())
 30      {
 31          var first = it.Current;
 32          while (it.MoveNext()) if (!equalityComparer.Equals(first, it.Current)) return false;
 33      }
 34      return true;
 35  }
 
You can always improve or modify to your taste (but maybe for the cost of runing twice over the first element):
 27      ...
 28      var first = source.FirstOrDefault();
 29      foreach (var item in source) if (!equalityComparer.Equals(first, item)) return false;
 30      return true;
 31  }
Or:
 27      ...
 28      var first = source.FirstOrDefault();
 29      return source.All(e => equalityComparer.Equals(first, e));
 30  }
 
BTW: You can not easily combine IEnumerable<T> and index-based for loops, and thus, Linq and index-based for do not easily combine neither.
 
If you stick on index-based for loop, then you have to implement different functions for the different more specific kind of collections (e.g. IList<T>, IDictionary<K, V>, HashSet<T>, etc.). You may not use any of the Linq functions since they return IEnumerable<T> that allows to chain them easily. An index-based loop needs an end-condition up-front, (something like Count()) and IEnumerable<T> is not random-access (i.e. does not allow to access an element by index). IEnumerable<T> goes hand in hand with iterators, but not with indices.
 
You can of course call Count(), but that already iterates over all elements to simply find out how often to move ahead in the sequence of elements...
 
As I said further above, we both have different coding styles and approaches. Luckily, there is no "one and only right" (dogmatic?) solution Wink | ;-)
 
Cheers
Andi
GeneralRe: How about a linq'ish approach?memberPIEBALDconsult19 May '12 - 5:20 
Andreas Gieriet wrote:
You can of course call Count()

 
Except for that there's no guarantee that a given IEnumerable will ever terminate.
Questiona problemmvpLuc Pattyn17 May '12 - 14:44 
Sorry to say, however I did read the tip again, and now I bumped into a real problem: if T is a reference type, first could be null, hence first.Equals(...) could go kaboom. And I'm curious to learn what the elegant solution will be...
 
D'Oh! | :doh:
Luc Pattyn [My Articles] Nil Volentibus Arduum

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 18 May 2012
Article Copyright 2012 by PIEBALDconsult
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid