By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,693 Members | 1,936 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,693 IT Pros & Developers. It's quick & easy.

Re: Should you simplify this code? Write only code

P: n/a
On Sep 9, 9:41*am, raylopez99 <raylope...@yahoo.comwrote:
Scott Meyers in his 2001 book on STL / C++ claims that the following
code is optimal--what is the C# way and should you follow his example
and try and do this in LINQ? *Pseudo code is OK, I'm just looking for
a conceptual answer.
It's tricky in .NET for two reasons:

1) LINQ doesn't have any concept of "remove"
2) List<T>.RemoveAll doesn't pass in the index (which makes sense as
it would then need to

If List<Tsupported some sort of "view" which also exposed RemoveAll,
it would be easy:

int lastIndex = list.FindLastIndex(value =value >= y);
list.SubList(lastIndex).RemoveAll(value =value < x);

But as "SubList" doesn't exist, I suspect in this case I'd have to go
back to a foreach loop.
In many other cases it *does* make sense to chain functions together -
there are just limitations in this particular question.
I personally don't like the C++, but that's as much to do with the
library design and lack of lambda expressions as anything else.
Note that if instead of removing data from the current list you were
happy with a new list with the retained elements, it's really easy:

int lastIndex = list.FindLastIndex(value =value >= y);
List<XnewList = list.Where((index, value) =index < lastIndex ||
value >= x).ToList();

Still two statements (to avoid finding the last index repeatedly) but
it's still sound. (I haven't tested the above, admittedly.)

Jon
Sep 9 '08 #1
Share this Question
Share on Google+
5 Replies


P: n/a
raylopez99 <ra********@yahoo.comwrote:
On Sep 9, 3:30*am, "Jon Skeet [C# MVP]" <sk...@pobox.comwrote:

Note that if instead of removing data from the current list you were
happy with a new list with the retained elements, it's really easy:

int lastIndex = list.FindLastIndex(value =value >= y);
List<XnewList = list.Where((index, value) =index < lastIndex ||
value >= x).ToList();
I tried this and it failed to compile because there is no .Where
operator for lists.
Something tells me you forgot "using System.Linq;"

List<Timplements IEnumerable<T>, so Where certainly *does* work.

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Sep 11 '08 #2

P: n/a
On Sep 10, 10:34*pm, Jon Skeet [C# MVP] <sk...@pobox.comwrote:
>
Something tells me you forgot "using System.Linq;"

List<Timplements IEnumerable<T>, so Where certainly *does* work.
Yes, I did forget System.Linq. Below is the demo program, it works.
I use two different ways: one, doing the "Linq" way you suggest, the
other, doing it through "delegate" type logic** (sorry if I have the
lingo wrong).

I think 'your' way* using anonymous types and lambda is 'easier', more
compact and works fine but only if you know the syntax. I could not
find anything on Online Help (F1 in Visual Studio 2008), except
through IntelliSense, how to get the syntax right, other than using
your suggestion in the previous thread, but during actual programming
I don't have your expertise around, unfortunately.

BTW, as a minor aside, if you can figure out a quick way of casting to
avoid this compiler error (see below), please let me know: "Cannot
implicitly convert type 'System.Collections.Generic.IEnumerable<int>'
to 'System.Collections.Generic.List<int>'. An explicit conversion
exists (are you missing a cast?)"

RL

* your way is this line below: "newList2 = newList2.Where((index,
value) =index < lastIndexJon && value >= x).ToList(); "
** the 'delegate logic' way is after this line below: //final way to
do it: use PredicateClass2
PS--ignore the first part of the program below, just pick up where the
program says "Console.WriteLine("start second example(s) here...")",
since the stuff before is from a slightly different thread. Also, the
x,y's are sometimes interchanged in the symbol variables, but the
point is simply this: the problem solution is to traverse a List of
integers, from 0 to 9 or so, then, using two 'pivots', namely five and
8, find all the elements in the List that are greater or equal to five
and less than eight. The answer is 5, 6 and 7, as shown.

/////////

// OUTPUT (output is as expected)
predicates - 9/11/2008
0 1 2 3 4 5 6 7 8 -10

lastIndex is: 8, firstIndex is 5
lastIndex2 is: 8
firstIndex2 is: 5
lastIndex3 is: 8
firstIndex3 is: 5
start second example(s) here...
lastIndexJon is 8

filtered newList2
i: 5 , i: 6 , i: 7 ,
queried newList3
query i: 5 query i: 6 query i: 7 query2's i: 5 query2's i: 6 query2's
i: 7
End of Program
Press any key to continue . . .
////////////

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{

//////////////////////////////////////////////////
// predicates - 9/11/2008

Console.WriteLine("\n predicates - 9/11/2008 ");
List<intnewList = new List<int>();

for (int i = 0; i < 10; i++)
{
newList.Add(i);
}
newList.Remove(9); //note: commenting out this line
gives: 0 1 2 3 4 5 6 7 8 -10 9 (ten elements) since nothing removed!
newList.Insert(9, -10); //gives output 0 1 2 3 4 5 6 7 8
-10
foreach (int i in newList)
{
Console.Write(" {0}", i);
}
Console.WriteLine("\n");
int y = 5;

int lastIndex = newList.FindLastIndex(value =value >=
y);
int firstIndex = newList.FindIndex(value =value >= y);
Console.WriteLine("lastIndex is: {0}, firstIndex is {1}",
lastIndex, firstIndex); //last index is 8 (i.e. List[8] = 8) first
index is 3 (List[3]= 3)

int lastIndex2 = newList.FindLastIndex(MyPredicate);
Console.WriteLine("lastIndex2 is: {0}", lastIndex2);
int firstIndex2 = newList.Find(MyPredicate);
Console.WriteLine("firstIndex2 is: {0}", firstIndex2);

int lastIndex3 = newList.FindLastIndex(new
PredicateClass(y).MyPredicate);
Console.WriteLine("lastIndex3 is: {0}", lastIndex3);
int firstIndex3 = newList.Find(new
PredicateClass(y).MyPredicate);
Console.WriteLine("firstIndex3 is: {0}", firstIndex3);

//////////////////////////////////////

Console.WriteLine("start second example(s) here...");

int lastIndexJon = newList.FindLastIndex(value =value >= y); //gives
value of '8'
Console.WriteLine("lastIndexJon is {0}", lastIndexJon);
List<intnewList2 = new List<int>();
for (int i = 0; i < 10; i++)
{
newList2.Add(i); //produces a list from (0 to 9)
}
List<intnewList3 = new List<int>(); //don't initialize, as this
catches what's filtered by the .where clause
List<intnewList4 = new List<int>();

int x = 5; //second pivot point

newList3 = newList2;
newList4 = newList2;

newList2 = newList2.Where((index, value) =index < lastIndexJon
&& value >= x).ToList();
// note .Where "overwrites" the newList2, no need to
create another new List

Console.Write("\n filtered newList2 \n");
foreach (int i in newList2)
{
Console.Write(" i: {0} ,", i);
}

IEnumerable<intquery = newList3.Where((index, value) =index <
lastIndexJon && value >= x).ToList();
//strangely, the LHS, 'query', is also equivalent to a list, so you
can put on the LHS a list, such as: List<intanotherList = new
List<int>(); anotherList = newList3.Where((index, value) =index <
lastIndexJon && value >= x).ToList();

Console.Write("\n queried newList3 \n");
foreach(int i in query)
{
Console.Write("query i: {0} ",i);
}

//final way to do it: use PredicateClass2
IEnumerable<intquery2 = newList4.Where(new PredicateClass2(x,
lastIndexJon).MyPredicate);

//unfortunately, you cannot convert the LHS 'query2' into a list
directly, as you can with variable 'query' above, as you get compiler
error: "Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<int>' to
'System.Collections.Generic.List<int>'. An explicit conversion exists
(are you missing a cast?)"
foreach (int i in query2)
{
Console.Write("query2's i: {0} ", i);
}
Console.Write("\n End of Program \n");

}

//////////////// end of Main() /////////////////

private static bool MyPredicate(int value)
{
if (value >= 5)
{ return true; }
else
{ return false; }

}

public class PredicateClass
{
private int _y;
public PredicateClass(int y)
{
_y = y;
}
public bool MyPredicate(int value)
{
if (value >= _y)
{ return true; }
else
{ return false; }
}

}

public class PredicateClass2
{
private int _y;
private int _x;
public PredicateClass2(int y, int x)
{
_y = y;
_x = x;
}
public bool MyPredicate(int value)
{
if (value >= _y && value < _x)
{ return true; }
else
{ return false; }
}

}
}

/*
*
List<stringfruits = new List<string{ "apple", "passionfruit",
"banana", "mango", "orange", "blueberry", "grape", "strawberry" };
IEnumerable<stringquery = fruits.Where(fruit =fruit.Length < 6);
foreach (string fruit in query)
Console.WriteLine(fruit);

This code produces the following output:

apple
mango
grape
*/

}

Sep 11 '08 #3

P: n/a
raylopez99 <ra********@yahoo.comwrote:
Something tells me you forgot "using System.Linq;"

List<Timplements IEnumerable<T>, so Where certainly *does* work.

Yes, I did forget System.Linq. Below is the demo program, it works.
I use two different ways: one, doing the "Linq" way you suggest, the
other, doing it through "delegate" type logic** (sorry if I have the
lingo wrong).

I think 'your' way* using anonymous types and lambda is 'easier', more
compact and works fine but only if you know the syntax. I could not
find anything on Online Help (F1 in Visual Studio 2008), except
through IntelliSense, how to get the syntax right, other than using
your suggestion in the previous thread, but during actual programming
I don't have your expertise around, unfortunately.
Yes, you need to know the syntax. That's the same with everything
though. If someone didn't know about the "using" directive, would you
suggest they just fully qualified every name with the namespace? I
doubt it - I think you'd suggest they should learn about the "using"
directive. Ditto lambda expressions. Yes, they're "new and different" -
but they're well worth learning, and really not very difficult.
BTW, as a minor aside, if you can figure out a quick way of casting to
avoid this compiler error (see below), please let me know: "Cannot
implicitly convert type 'System.Collections.Generic.IEnumerable<int>'
to 'System.Collections.Generic.List<int>'. An explicit conversion
exists (are you missing a cast?)"
You don't cast to avoid the compiler error - you either call ToList()
to create an actual list, or just work with the IEnumerable<int>. The
value returned from Where *isn't* a List.

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Sep 11 '08 #4

P: n/a
On Sep 11, 6:53*am, Jon Skeet [C# MVP] <sk...@pobox.comwrote:
>
You don't cast to avoid the compiler error - you either call ToList()
to create an actual list, or just work with the IEnumerable<int>. The
value returned from Where *isn't* a List.
Yes, that's right. I tried this and it works now:

//change this: IEnumerable<intquery2 = newList4.Where(new
PredicateClass2(x, lastIndexJon).MyPredicate);

//to this:

List<intnewList234 = new List<int>();

newList234 = newList4.Where(new PredicateClass2(x,
lastIndexJon).MyPredicate).ToList(); //<--note .ToList() added, now a
list

And newList234 is now a list.

Curious how long it took you to learn LINQ syntax--seems no references
exist out there, and the examples given in books (including your own)
are simplistic (seems).

RL
Sep 11 '08 #5

P: n/a
On Sep 11, 10:18*pm, Jon Skeet [C# MVP] <sk...@pobox.comwrote:
Basically:

Deferred = will execute later, when you ask for data
Immediate = executes now

Buffered = needs to read to the end of the input data before yielding
any results (e.g. Reverse)
Streaming = can yield data as it reads it, e.g. Select or Where.
I see these terms bandied about but nobody ever directly explains (or
it's rather oblique) as to when to use buffered, when to use
streaming, etc. Conceptually, buffered takes more memory, and
probably takes longer to run, since you have to construct a temporary
container to store the data, the replicate the data, then read out,
while streaming is faster and takes memory, since you only are
traversing your original data "on the fly". But with streaming you
cannot "back up" or randomly access data, you have to take whatever
the stream position is giving you.

It would be helpful to know when to buffer, when to stream. Most
books tend to caution against using too many loops (buffer) or copying
to many structures (ditto), so read-once sequential 'streaming' seems
to be the default preferred choice. That said, this following code,
setting up a .ToArray() (Buffer) has saved me from a compiler error--

foreach (MyClass X in MyClassList.ToArray()) //this avoids
the runtime error: " Collection was modified; enumeration operation
may not execute." -creates a temporary .ToArray() in loop for
enumeration purposes only, which is the standard solution to this
problem
{
// do stuff that changes MyClassList (a list containing MyClass
parameters/values) here
}

RL
Sep 14 '08 #6

This discussion thread is closed

Replies have been disabled for this discussion.