Dictionary ForAll / ForEach method
What is the proper ForAll
method for the Dictionary
object? I am learning the Linq type iteration (method 1 below), but do not getting it to work. The old foreach
loop works (method 2 below). Despite many answers on similar questions, eg this question, I do not see a Linq iteration solution.
The Dictionary seems to have methods to get all Keys or all Values, usable in the Linq iteration, but not to get all KeyValuePairs. Strange!
I did some fiddling with GetEnumeration()
, but without much luck.
The AsParallel()
method seemed the solution, found here, but with me I only got a build error: Dictionary does not contain a definition for AsParallel .
One could argue that Method 2 does not have so much more lines of source, but to me the Linq iteration of Method 1 is still more simple, more direct, more elegant.
Is there still an easy way to make Method 1 work?
Version: .Net Framework 4.5.2
using System.Linq;//AsParallel, ToList
using System.Collections.Generic;//Dictionary
using System.Xml.Linq;//XElement
using System.IO;//Path, Dir, File
public Dictionary<string, string> DictUserConfig = new Dictionary<string, string>();
public void SaveFile(string FileName)
{
XDocument XDConf = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
#if true//method 1: problem: Dictionary does not have a .ForAll or .ForEach method, or .GetEnumerator().ForAll(x => ...)
XDConf.Add(
new XElement("settings",
DictUserConfig.ForEach(x =>
new XElement("add",
new XAttribute("key", x.Key),
new XAttribute("value", x.Value)))));
#else//method 2: works
XElement XSettings = new XElement("settings");
XDConf.Add(XSettings);
foreach (KeyValuePair<string, string> x in DictUserConfig)
{
XSettings.Add(
new XElement("add",
new XAttribute("key", x.Key),
new XAttribute("value", x.Value)));
}
#endif
XDConf.Save(FileName);
return;
}
Update: I discovered one problem: missing using System.Linq
, removed some time ago when I didn't need that. Discovered by testing the several useful comments on my post.
Now the problem is more easily summarized as:
This works:
DictUserConfig.ToList<KeyValuePair<string, string>>().ForEach(x => Console.WriteLine(x.Key + x.Value));
DictUserConfig.AsParallel().ForAll(x => Console.WriteLine(x.Key + x.Value));
But this does not work:
XElement XE1 = (XElement)DictUserConfig.ToList<KeyValuePair<string, string>>().ForEach(x => new XElement(x.Key, x.Value));
XElement XE2 = DictUserConfig.AsParallel().ForEach(x => new XElement(x.Key, x.Value));
And the reason is, I suppose, that ToList and AsParallel do not return the result of the ForEach back to the this
. The build error is: cannot convert void to object (or XElement). So the only 'solution' seems to create ForEach myself using the one-liner, which is already showing as a solution, but taking care to return an XElement object.
After testing the final answer, I would like to note that the discussion is not about the relative values of ForEach versus foreach, but that this obscured the real solution of Select. Also, foreach is more the Fortran way of making something happen, and ForEach, and especially Select, rather about what you want to achieve.
I would like to thank everyone for the comments and answers, of which I learned something today.
For each KeyValuePair<string,string>
you want to create and return a new XElement
with proper attributes. This is called a projection .
Select
is LINQ
's projection method:
XDConf.Add(new XElement("settings",
DictUserConfig.Select(
kvp => new XElement("add",
new XAttribute("key", kvp.Key),
new XAttribute("value", kvp.Value)))/*.
Cast<object>().ToArray()*/));
The cast at the end is necessary to call the correct constructor of XElement
.
Note that List<T>.ForEach
iterates over the enumeration and calls an Action<KeyValuePair<TKey,TValue>>
for each KeyValuePair
. But it does not return anything, so you cannot use it in your XElement
constructor call.
You can use the ToList extension method in order to get a list of KeyValuePairs that is compatible with the ForEach method.
Dictionary<string, int> testDict = new Dictionary<string, int>();
testDict.Add("one", 1);
testDict.Add("two", 2);
testDict.Add("three", 3);
testDict.ToList<KeyValuePair<string, int>>().ForEach(x => Console.WriteLine(x.Key));
DotNetFiddle here: https://dotnetfiddle.net/5rRGZc
I would agree with the comment by Sakura that the normal foreach is probably the better choice, though.
ForEach()
is a specific method of List<T>
class.
But you can make your own extensor method of Dictionary<TKey, TVakue>
:
public static class DictionaryExtensions
{
public static void ForEach<TKey, TValue>(this Dictionary<TKey, TValue> dict, Action<KeyValuePair<TKey, TValue>> action)
{
foreach (var item in dict)
action(item);
}
}
And then use it like this:
XElement XSettings = new XElement("settings");
DictUserConfig.ForEach(x =>
XSettings.Add(new XElement("add",
new XAttribute("key", (string)x.Key),
new XAttribute("value", (string)x.Value))));
UPDATE :
Using method 1, then you can use an extensor like this:
public static IEnumerable<TResult> ForEach<TKey, TValue, TResult>(this Dictionary<TKey, TValue> dict, Func<KeyValuePair<TKey, TValue>, TResult> func)
{
foreach (var item in dict)
yield return func(item);
}
Then method 1 would be:
XDConf.Add(new XElement("settings",
DictUserConfig.ForEach(x =>
new XElement("add", new XAttribute("key", x.Key),
new XAttribute("value", x.Value))).ToArray()));
Probably a Cast<object>()
would be necessary before ToArray()
.