How to sort List<...> -- created using LinQ?

  • Thread starter Thread starter Rich P
  • Start date Start date
R

Rich P

I found a nice LinQ function for GetFiles with multiple search patters
(multiple file types -- *.jpg, *.bmp, *.gif). This seems to work nicely
except that my list of files has an alphabetical ordering like

a1.jpg
a2.jpg
a1.bmp
a2.bmp
b1.jpg
b2.jpg
b1.bmp
b2.bmp
...

and the routine is listing/sorting all the .jpg's first and then the
.bmps, .gifs.

a1.jpg
a2.jpg
b1.jpg
b2.jpg
a1.bmp
a2.bmp
b1.bmp
b2.bmp

In windows explorer the files are listed/sorted by filename
alphabetically as desired.

a1.bmp
a1.jpg
a2.bmp
a2.jpg
b1.bmp
b1.jpg
b2.bmp
b2.jpg
...

Is there a way to sort this list the same as Windows Explorer? Or --
this doesn't have to be a List by LinQ. Using the FileSystem I can add
all the search patterns inline and it sorts like Windows Explorer, but I
was advised that Directory.GetFiles is more efficient. Any suggestions
appreciated what would be the best way to GetFiles and sort them like
Windows Explorer.

//here is the LinQ GetFiles routine I found on the net
public List<string> GetFiles(string dir, List<string> patterns)
{
List<string> matches = new List<string>();

//loop thorugh all extensions provided
foreach (string pattern in patterns)
{
//use LINQ to get each file with the soecified file type
var matchingFiles = from file in Directory.GetFiles(dir,
pattern,SearchOption.AllDirectories)
select file;

//now add all files to our list
matches.AddRange(matchingFiles);
}

return matches;
}

//here is how I call it
private void btnSlideShow_Click(object sender, EventArgs e)
{
List<string> Patrns = new List<string>();
Patrns.Add("*.jpg");
Patrns.Add("*.bmp");
Patrns.Add("*.gif");
List<string> fil = new List<string>(GetFiles(lblCurDir.Text,Patrns));

foreach (string str1 in fil)
Console.WriteLine(str1);
}


Thanks

Rich
 
Rich P said:
I found a nice LinQ function for GetFiles with multiple search patters
(multiple file types -- *.jpg, *.bmp, *.gif). This seems to work nicely
except that my list of files has an alphabetical ordering like

a1.jpg
a2.jpg
a1.bmp
a2.bmp
b1.jpg
b2.jpg
b1.bmp
b2.bmp
..

and the routine is listing/sorting all the .jpg's first and then the
bmps, .gifs.

a1.jpg
a2.jpg
b1.jpg
b2.jpg
a1.bmp
a2.bmp
b1.bmp
b2.bmp

In windows explorer the files are listed/sorted by filename
alphabetically as desired.

a1.bmp
a1.jpg
a2.bmp
a2.jpg
b1.bmp
b1.jpg
b2.bmp
b2.jpg
..

Is there a way to sort this list the same as Windows Explorer? Or --
this doesn't have to be a List by LinQ. Using the FileSystem I can add
all the search patterns inline and it sorts like Windows Explorer, but I
was advised that Directory.GetFiles is more efficient. Any suggestions
appreciated what would be the best way to GetFiles and sort them like
Windows Explorer.

//here is the LinQ GetFiles routine I found on the net
public List<string> GetFiles(string dir, List<string> patterns)
{
List<string> matches = new List<string>();

//loop thorugh all extensions provided
foreach (string pattern in patterns)
{
//use LINQ to get each file with the soecified file type
var matchingFiles = from file in Directory.GetFiles(dir,
pattern,SearchOption.AllDirectories)
select file;

//now add all files to our list
matches.AddRange(matchingFiles);
}

return matches;
}

//here is how I call it
private void btnSlideShow_Click(object sender, EventArgs e)
{
List<string> Patrns = new List<string>();
Patrns.Add("*.jpg");
Patrns.Add("*.bmp");
Patrns.Add("*.gif");
List<string> fil = new List<string>(GetFiles(lblCurDir.Text,Patrns));

foreach (string str1 in fil)
Console.WriteLine(str1);
}


Thanks

Rich

OrderBy?

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.orderby.aspx

And someone giving an example in practice:

http://www.singingeels.com/Blogs/Nullable/2008/03/26/Dynamic_LINQ_OrderBy_using_String_Names.aspx
 
Rich said:
//here is how I call it
private void btnSlideShow_Click(object sender, EventArgs e)
{
List<string> Patrns = new List<string>();
Patrns.Add("*.jpg");
Patrns.Add("*.bmp");
Patrns.Add("*.gif");
List<string> fil = new List<string>(GetFiles(lblCurDir.Text,Patrns));

foreach (string str1 in fil)
Console.WriteLine(str1);
}


Thanks

var sort = from s in fil orderby s
select s;

foreach(var str in sort)
{
//do something with str
}
 
Rich P said:
I found a nice LinQ function for GetFiles with multiple search patters
(multiple file types -- *.jpg, *.bmp, *.gif). This seems to work nicely
except that my list of files has an alphabetical ordering like

Hi Rich, that is a lot of code you've got there. The idea of linq (or one of
the ideas) is to reduce code. Everything you've done can be reduced to 2
lines

string[] patterns = new string[] {"*.jpg", "*.bmp", "*.gif" };
string[] results = patterns.SelectMany(p => Directory.GetFiles("C:\\",
p)).OrderBy(i => i).ToArray();

note that you don't have to use the ToArray method but could just do this
instead

var results = patterns.SelectMany(p => Directory.GetFiles("C:\\",
p)).OrderBy(i => i);
foreach(string res in results) etc
 
var results = patterns.SelectMany(p => Directory.GetFiles("C:\\",
p)).OrderBy(i => i);
foreach(string res in results) etc
<<

Nice ! :)

Thank you all for your replies. Yes, I think I am starting to like
linQ!

Rich
 
string[] patterns = new string[] {"*.jpg", "*.bmp", "*.gif" };

var results = patterns.SelectMany(p => Directory.GetFiles("C:\\",
p)).OrderBy(i => i);
this will be a function

public something GetMyFiles(string dir)
{
...
return (something) results;
}


What is the something cast as ? What will receive "results" ? A
string[]? A List<string> ?

Thanks.
Rich
 
Rich P said:
What is the something cast as ? What will receive "results" ? A
string[]? A List<string> ?

You decide. You've got the choice of string array, list of strings, an
enumerable or whatever. I think a list is out because generally you don't
need to pass back something which is modifyable. An enumerable would be ok,
but not so common. For an enumerable you would pass back
IEnumerable<string>. I think though an array would possibly be the right
choice. There is no right answer really.

Michael
 
BTW, here's my favourite extension to add to linq. Just paste the code into
your project somewhere and then ForEach becomes a linq method, eg

patterns.SelectMany(p => Directory.GetFiles("C:\\",
p)).OrderBy(i => i).ForEach(f => Console.WriteLine(f));

The cool thing is you can get your data, manipulate it and then use it all
on the one line of code.


Cheers,
Michael



public static void ForEach<TSource>(this
System.Collections.Generic.IEnumerable<TSource> source, Action<TSource>
action)
{
ThrowIfNull(source, "source");
ThrowIfNull(action, "action");

foreach (TSource item in source)
{
action(item);
}
}

public static void ForEach<TSource>(this
System.Collections.Generic.IEnumerable<TSource> source, Action<TSource, int>
action)
{
ThrowIfNull(source, "source");
ThrowIfNull(action, "action");

int index = 0;
foreach (TSource item in source)
{
action(item, index);
index++;
}
}
 
Thanks for getting back to me, but I am a little bit out of my league
here. This is some pseudo code of how I would like to use the sample
string[] patterns = new string[] {"*.jpg", "*.bmp", "*.gif" };

var results = patterns.SelectMany(p => Directory.GetFiles("C:\\",
p)).OrderBy(i => i);

<<


public something GetMyFiles(string dir)
{
string[] patterns = new string[] {"*.jpg", "*.bmp", "*.gif" };

var results = patterns.SelectMany(p =>
Directory.GetFiles(dir,patterns)).OrderBy(i => i);

return results;
}

public void btn_click(...)
{
string dir = @"C:\dir1";
something x = (something)GetMyFiles(dir);
foreach(something y in x)
Console.WriteLine(y)'
}


How can I make something like this work?

Thanks.

Rich
 
You can do this for an array
public string[] GetMyFiles(string dir)
{
string[] patterns = new string[] {"*.jpg", "*.bmp", "*.gif" };

string[] results = patterns.SelectMany(p =>
Directory.GetFiles(dir, p)).OrderBy(i => i).ToArray();

return results;
}

public void btn_click(...)
{
string dir = @"C:\dir1";
string[] x = GetMyFiles(dir);
foreach(something y in x)
Console.WriteLine(y)'
}

Or for for a list (changed lines only):
public List<string> GetMyFiles(string dir)
List<string> results = patterns.SelectMany(p =>
Directory.GetFiles(dir, p)).OrderBy(i => i).ToList();
List<string> x = GetMyFiles(dir);

Or to return an Enumerable:
public IEnumerable<string> GetMyFiles(string dir)
IEnumerable<string> results = patterns.SelectMany(p =>
Directory.GetFiles(dir, p)).OrderBy(i => i);
IEnumerable<string> x = GetMyFiles(dir);

You had the variable patterns inside the GetFiles call when this should be
p.
 
Or for for a list (changed lines only):
public List<string> GetMyFiles(string dir)
List<string> results = patterns.SelectMany(p =>
Directory.GetFiles(dir, p)).OrderBy(i => i).ToList();
List<string> x = GetMyFiles(dir);

Or to return an Enumerable:
public IEnumerable<string> GetMyFiles(string dir)
IEnumerable<string> results = patterns.SelectMany(p =>
Directory.GetFiles(dir, p)).OrderBy(i => i);
IEnumerable<string> x = GetMyFiles(dir);
<<

Cool! Now I am seeing it. Thank you very much

Question: is there any criteria if I would want to use a List vs
IEnumerable? Or is it a "more than one way to do it" thing?

If there is a reason I would want to use one method over the other --
may I ask what would be a reason/criteria for one over the other?

Thanks again for the help.

Rich
 
Rich P said:
Cool! Now I am seeing it. Thank you very much

Question: is there any criteria if I would want to use a List vs
IEnumerable? Or is it a "more than one way to do it" thing?

If there is a reason I would want to use one method over the other --
may I ask what would be a reason/criteria for one over the other?

Thanks again for the help.

The thing that makes the IEnumerable different is something called "Deferred
execution". This means it won't actually execute the linq query until you
use it in a foreach statement. If you use it again in another for each
statement then it will execute it again. This gives the advantage that you
can create the query and then execute it multiple times looking for a
change. The disadvantage is that it needs to execute each time so could be
slower and in some cases you might not want the chance of different results
each time. With the list or array the query will execute only once so you
will get the same results each time.

In 99% of cases I would return an array but there are cases where returning
the IEnumerable would make sense. As an example, I had a linq query to
search for data in the grid usercontrol built into dot net. I created the
query and assigned it to a module level variable and executed it each time a
user searched.

Michael
 
Back
Top