views:

718

answers:

3

I'm trying to use LINQ To Objects to create a query that will give me files, indexed by filename with a value mapping to their binary data as byte[].

However I can't find a 'neat' way to do this. I'm hoping to get something like a Dictionary<T,K> output.

Here's what I have so far. Example delimFileNames="1.jpg|2.jpg"

//Extract filenames from filename string
//and read file binary from file
//select result into a filename indexed collection
var result = from f in delimFileNames.Split(Constants.DDS_FILENAME_SEPARATOR)
            let filePath = Path.Combine(ddsClient.WorkingDirectory, f)
            let fileData = File.ReadAllBytes(filePath)
            select new KeyValuePair<string, byte[]>(f, fileData);

return result.ToDictionary(kvp => kvp.Key, kvp=> kvp.Value);

The main head scratcher is why I can't use a parameterless ToDictionary(), or a direct cast. Any suggestions or alternatives to improve the above are appreciated.

+2  A: 

ToDictionary() would have to have a special case for KeyValuePair - which you could add as your own extension method of course:

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
     IEnumerable<KeyValuePair<TKey, TValue>> source)
{
    return source.ToDictionary(kvp => kvp.Key, kvp=> kvp.Value);
}

Without that extension method, you could do it like this:

var result = from f in delimFileNames.Split(Constants.DDS_FILENAME_SEPARATOR)
                                     .ToDictionary(f => f,
          // Outdented to avoid scrolling
          f => File.ReadAllBytes(Path.Combine(ddsClient.WorkingDirectory, f));

Basically the query expression allows you to do all the funky "let" stuff, but forces you to have a select at the end, which makes you propagate the two different values (the key and the byte array) into two properties.

Jon Skeet
+1 I think you lurk around waiting for these questions Jon :P
Kyle Rozendo
A: 

"The main head scratcher is why I can't use a parameterless ToDictionary() [...]"

Because the ToDictionary method needs to know what is the key and what is the value. Microsoft could have created an overload for this special case but didn't, because the benefit is not very big I suppose. But nothing stops you from creating your own extension method that does exactly that.

"[...] or a direct cast."

Because your Linq query returns an IEnumerable<KeyValuePair<TKey, TValue>> which is not a Dictionary<TKey, TValue>. It allows you to enumerate through the key/value pairs, but doesn't allow quick lookup by key.

Note that the Dictionary<TKey, TValue> IS an IEnumerable<KeyValuePair<TKey, TValue>> though (it implements that interface), which means that you can iterate through the key/value pairs just like you did with the Linq query, but the underlying type is a Dictionary, so you can get quick lookup by key.

Meta-Knight
A: 

You don't need to use KeyValuePair<,>, instead you can use an anonymous type which allows you to use more descriptive names than Key and Value:

var files =
    from fileName in delimFileNames.Split(Constants.DDS_FILENAME_SEPARATOR)
    let filePath = Path.Combine(ddsClient.WorkingDirectory, fileName)
    select new
    {
        Path = filePath,
        Data = File.ReadAllBytes(filePath)
    };

return files.ToDictionary(file => file.Path, file => file.Data);
Bryan Watts