tags:

views:

210

answers:

4

I'm reading a C# book for beginners, and in every end of the chapter, there are exercises to be answered based on the lessons tackled.

One of those exercises goes this way: (not the exact wordings)

Write a program that will accept an int as the array length, and the values for the array.
Then will print:
"0" if the array is not sorted in ascending way.
"1" if it is sorted. And,
"2" if it is sorted, but there are duplicates.

Example:

// Sorted
Input: 1, 2, 3, 5
Print: 1

// Not sorted
Input: 2, 1, 3, 6
Print: 0

// Sorted, but with duplicates
Input: 2, 2, 3, 7
Print: 2

I don't know if my logic here is absolute, but somehow it is working,
and I done it in my way using this code:

int arrayLength = 0;
int prev, next;
int sortStatus = 1;

Console.Write("Input array Length: ");
arrayLength = Convert.ToInt32(Console.ReadLine());
int[] ar = new int[arrayLength];

for (int x = 0; x < arrayLength; x++)
{
    Console.Write("Input {0} value: ", (x+1).ToString());
    ar[x] = Convert.ToInt32(Console.ReadLine());
}

for (int x = 0; x < ar.Length-1; x++)
{
    prev = (int)ar[x];
    next = (int)ar[x + 1];

    if (next < prev)
        sortStatus = 0;
    if (next == prev)
        sortStatus = 2;
}

Console.Write(sortStatus.ToString());
Console.Read();

Is it possible to express this in LINQ? How?

+14  A: 
if (ar.SequenceEqual(ar.OrderBy(x => x)))
{
    if (ar.Distinct().Count() == ar.Length)
        return 1;
    else
        return 2;
}
else 
{
    return 0;
}
Kirk Woll
Thanks sir, I'll try yours...
yonan2236
Yep, this is the way to go. Nice work. +1
RPM1984
it works sir... :) thanks.
yonan2236
I bet your original code runs faster! ;)
Mitch Wheat
@Mitch Wheat: really? Then I should stick to it?
yonan2236
@yonan, as a general rule, non-LINQ code will outperform LINQ code, assuming you're writing each in the smartest possible way. But also keep in mind that LINQ allows us to be more expressive, so the trade off is often worth it. Also consider that unless you are writing a performance-intensive application or you identify the LINQ code as a real bottleneck, then worrying about minor performance differences is an opportunity cost. If you have other things to do, do those other things.
Anthony Pegram
@yonan, but for full disclosure, consider what your code is doing. You are looping over the array one time. Now look at LINQ. `OrderBy` is a multiple-iteration operation. `SequenceEqual` is an iteration of the array. `Distinct()` also iterates over the array. `Count()` would, except in this case, LINQ will optimize it because of the nature of this particular collection. Even still, you have multiple iterations over your array to make the LINQ code give you the result your code is doing in one pass.
Anthony Pegram
@Anthony Pegram: Well said Sir. I got your thought. I can't just understand those `=>` expressions... Maybe I need a good book on LINQ. Can you recommend one Sir?(for beginners)
yonan2236
@yonan.. For beginners? Maybe go with Essential C# 4.0. It targets all levels, but specifically highlights sections for beginners, advanced, etc., inline with the rest of the text. Jon Skeet's C# In Depth is fabulous, but it assumes you have some experience. But it really is wonderful for the evolution of the language, dealing with delegates, anonymous methods, lambdas (you will note we were stuck with delegates in C# 1, got anonymous methods in C# 2 to simplify it a bit, got `Func<>` and lambdas in C# 3 to simplify it even further) and how it all comes together for LINQ.
Anthony Pegram
Ahh.. how I wish I will find some copy of those books in our country...
yonan2236
+2  A: 

A pure LINQ alternative ... (for academic interest only (but probably still faster than the accepted answer!)

var input = new int[] { 1, 2, 3, 4, 5 };

var output = input.Zip(input.Skip(1), (a, b) => new {a=a, b=b})
                .Aggregate(1, (status, x) => status == 0 ? 0 : ((x.a > x.b ? 0 : (x.a == x.b ? 2 : status))));
Hightechrider
Hmm.. what is `input` here? Is it my array?
yonan2236
Yes, input would be your array.
Hightechrider
thanks sir for your version of answer :)
yonan2236
+1 for O(n) time complexity, unlike accepted answer.
Matajon
+3  A: 

As a note, your expressed non-LINQ logic has a flaw.

if (next < prev) 
    sortStatus = 0; 
if (next == prev) 
    sortStatus = 2; 

Your rule says that the array must be sorted ascending but have duplicates in order to get an output of 2. However, your logic will return 2 for { 1, 9, 7, 7 }.

Another way to write your code might be the following. (This is not using LINQ, but this is too long to post as a comment to your question.)

static int EvaluateArray(int[] array)
{
    int? lastItem = null;
    bool match = false;
    foreach (int item in array)
    {
        if (item < lastItem)
            return 0;
        else if (item == lastItem)
            match = true;

        lastItem = item;
    }

    if (match)
        return 2;

    return 1;
}

In this method, we will early-return as soon as we have an item less than the previous item. Otherwise, we will set a boolean if we come across a matching value. At the end of the loop, we know the array is sorted ascending. The only thing left is check if there was a match.

Anthony Pegram
Thank you for bringing up my mistake. I will learn from it.
yonan2236
Sir just a question, in your code above, what does this code snippet means? `int? lastItem = null;`. The `"?"` thing there...
yonan2236
@yonan: That is shorthand for `Nullable<int>`. C# 2 introduced the concept of nullable value types. Normal value types such as `int` cannot be set to null, which resulted in developers using "magical" values to often mean "no value." (Consider `string.IndexOf(substring)` returns -1 if the substring is not found within the string.) With `Nullable<T>`, we now have a way to actually indicate something has no value. In C#, we can express that with shorthand. `int?` is shorthand for `Nullable<Int32>` much like `int` is actually C# shorthand for `Int32`.
Anthony Pegram
Other shorthands include `double?, long?, float?, decimal?, DateTime?`, etc. Lookup `Nullable<T>` to find documentation for it, but know that it is basically a wrapper over a value type with two exposed properties: `Value` and `HasValue`. Saying `int? foo = null;` will result in `HasValue` being false, `int? foo = 7` will result in `HasValue` being true.
Anthony Pegram
I didn't know that...but now I do. Do you write books Sir? You could be a great author. Thanks again for the explanations. :)
yonan2236
Trust me, I'm only telling you what people were kind enough to tell me through their books, blogs, and answers here. Good luck with C#!
Anthony Pegram
Thanks! Good day, or Good night. :)
yonan2236
@Anthony: `foo?` will expand to `Nullable<foo>` for all values of foo. It's one shorthand, not a collection of shorthands. I know I'm picking on semantics, but your comment implies that there's a limited set of these, which there aren't, as it works for all types.
Nathan Tomkins
@Nathan, I was providing examples from the BCL followed by "etc." to indicate the list continued. But, yes, you are correct, you can write a custom *struct*, use ?, and boom! You have `Nullable<Bar>`. Note: *struct*. Does not work for all *types*, as classes are already nullable and need no `Nullable<>` wrapper.
Anthony Pegram
@Anthony: Right you are. I suppose if I'm going to argue a point like that I should make sure I'm totally right!
Nathan Tomkins
A: 

Untested.

IEnumerable<int> signs = 
  from i in Enumerable.Range(0, ar.Length).Skip(1)
  select ar[i-1].CompareTo(ar[i]);

int result =
  signs.Any(sign => sign < 0) ? 0 :
  signs.All(sign => 0 < sign) ? 1 :
  2;

Also untested:

int minSign = !ar.Skip(1).Any() ? 1 :
(
  from i in Enumerable.Range(0, ar.Length).Skip(1)
  select ar[i-1].CompareTo(ar[i])
).TakeWhile(x => 0 <= x).Min();

int result =
  minSign < 0 ? 0 :
  0 < minSign ? 1 :
  2;
David B
Please forgive my ascii art... actually, let me know if that bit is confusing - (x => 0 <= x)
David B
thanks for the answer :)
yonan2236