Tomatin
Tomatin

Reputation: 87

How to get a string list using linq?

I have a in C# a String[] with a list of folders. And I want to get a sublist depending on a criteria. For example:

var folder_list = new String[] {
    "FOLDER0001", 
    "FOLDER0002", 
    "FOLDER0003", 
    "FOLDER0004", 
    "FOLDER0005"
};

and with parameters (from and to) get the sublist

For example
from:1
to:3

I wish to get String[] sub_list = {"FOLDER0001", FOLDER0002", "FOLDER0003"}

I would very much like to do it using linq.

Upvotes: 0

Views: 931

Answers (5)

Tomatin
Tomatin

Reputation: 87

thank you very much everyone for the responses. Sorry for my english :(

I may not explain the problem correctly ... Basically I receive a fixed list of folders that I must compress and send by email. Because the SMTP server has attachment size limitations, I am forced to split the folder list into multiple zip files, But folders LIST may not be consecutive. Example:

var folder_list = new String[] {
"ABCDEF000001", 
"ABCDEF000002", 
"ABCDEF000004", 
"ABCDEF000005",
"ABCDEF000007",

};

So if I want the list from 1 to 3 I should get "ABCDEF000001", "ABCDEF000002"

My solution is similar to the one I implement Harald and John Wu

private String[] get_sub_list_folders(Int32 since, Int32 to, String[] folder_list)
{
  var result = from folder in folder_list
               where (Int32.Parse(folder.Substring(8)) >= since && Int32.Parse(folder.Substring(8)) <= to)
               select folder;

  return (result.ToList().ToArray());
}

private void zip_folders()
{
  const String folder_pattern = "ABCDEF";
  const Int32 folders_in_zip = 5000;

  // Create list of all folder
  String[] folder_list = Directory.GetDirectories(".", folder_pattern + "*", SearchOption.TopDirectoryOnly);

  // Just in case order by name 
  Array.Sort(folder_list);

  Int32 folders_count = folder_list.Length;
  Int32 from = 1;
  Int32 to = folders_in_zip;

  // Build a chunk of the folder list 
  while (folders_count > 0)
  {
    // Make a subset fixed folder list
    String[] zip_folders_list = get_sub_list_folders(from, to, folder_list);

    // Build zip name
    String zip_name = String.Format("{0}{1:0000000}_{0}{2:0000000}.zip", folder_pattern, from, to);

    // Create zip 
    zip.create(zip_folders_list, zip_name);

    from += folders_in_zip;
    to += folders_in_zip;
    folders_count -= folders_in_zip;
  }
}

Upvotes: 0

Harald Coppoolse
Harald Coppoolse

Reputation: 30454

For every folder name, extract the key, parse the key to a number and keep only the folder name if the number is between min and max

Using extension methods. See extension methods demystified

public string ExtractKeyText(this string folderName)
{
     // TODO: decide what to do if folderName == null

     // depending on your actual folder name.
     // is it really just "Folder00001" etc?
     return folderName.SubString(6);

     // if more difficult folderNames, consider using Regular Expressions
}

public IEnumerable<string> WhereFolderKeyBetween(
    this IEnumerable<string> folders,
    int minKey,
    int maxKey)
{
    // TODO: decide what to do if folders == null, or minKey > maxKey
    return folders.Where(folder => 
    {
        string keyTxt = folder.ExtractKeyText();
        return int.TryParse(keyTxt, out int key)
            && minKey <= key && key <= maxKey;
    });
};
        

Usage:

IEnumerable<string> folderNames = ...
IEnumerable<string> folderNamesWithinRange = folderNames.WhereFolderKeyBetween(3, 7);
    

Upvotes: 1

NetMage
NetMage

Reputation: 26907

Since you have a range to index you can use that with LINQ to extract the elements:

var sublist = Enumerable.Range(from-1, to-from+1).Select(i => folder_list[i]).ToArray();

But it would be much more efficient to use ArraySegment instead:

var sl = new ArraySegment<string>(folder_list, from-1, to-from+1).ToArray();

Upvotes: 0

John Wu
John Wu

Reputation: 52210

You'd need to use the integer input to reproduce the matching string(s) in order to search the strings by range.

var folder_list = new String[] {
    "FOLDER0001", 
    "FOLDER0002", 
    "FOLDER0003", 
    "FOLDER0004", 
    "FOLDER0005"
};


string[] GetSublist(int from, int to)
{
    string start = "FOLDER" + from.ToString("D4");
    string end   = "FOLDER" + to.ToString("D4");
    return folder_list.Where( x => (x >= start && x <= end)).ToArray();
}

Another option is to change the way you store the data to make it more searchable. This would perform slightly better and be more resilient to changes in the string formatting.

var folder_list = new Dictionary<int,string> {
    { 1, "FOLDER0001"}, 
    { 2, "FOLDER0002"}, 
    { 3, "FOLDER0003"}, 
    { 4, "FOLDER0004"}, 
    { 5, "FOLDER0005"}
};


string[] GetSublist(int from, int to)
{
    return folder_list
        .Where
        (
            x => (x.Key >= from && x.Key <= to)
        )
        .Select( x => x.Value )
        .ToArray();
}

Upvotes: 1

thatguy
thatguy

Reputation: 22079

Linq has the Skip method to leave out a number of elements in a sequence and Take to get a number of elements. The number that you pass is the count of elements that should be skipped or taken. For your example:

String[] subArray = folder_list.Take(3).ToArray();

You can also generalize this. Since you identify the first element with 1 instead of 0, the query would look like this.

public String[] SubArray(String[] array, int from, int to)
{
    return array.Skip(from - 1).Take(to).ToArray();
}

Upvotes: 2

Related Questions