Reputation: 193462
I have text documents like the following which contain single and multiple variables:
title:: Report #3
description:: This is the description.
note:: more information is available from marketing
note:: time limit for this project is 18 hours
todo:: expand the outline
todo:: work on the introduction
todo:: lookup footnotes
I need to iterate through the lines of this text document and fill a collection with these variables, currently I'm using a Dictionary:
public Dictionary<string, string> VariableNamesAndValues { get; set; }
But this doesn't work on multiple, identical keys such as "note" and "todo" in the above example since keys have to be unique in a Dictionary.
What is the best collection so that I can not only get single values like this:
string variableValue = "";
if (VariableNamesAndValues.TryGetValue("title", out variableValue))
return variableValue;
else
return "";
but that I can also get multiple values out like this:
//PSEUDO-CODE:
List<string> variableValues = new List<string>();
if (VariableNamesAndValues.TryGetValues("note", out variableValues))
return variableValues;
else
return null;
Upvotes: 8
Views: 4067
Reputation: 2973
You may use PowerCollections which is an open source project that has a MultiDictionary data structure which solves your problem.
Here is a sample of how to use it.
Note: Jon Skeet suggested it before in his answer to this question.
Upvotes: 2
Reputation: 19273
You can make a Dictionary of key: string and value: List of String
Dictionary<string,List<string>>
EDIT 1 & 2:
I've thought of a better solution if you can use .NET 3.0 or higher.
Here's a LINQ example (I typed it without Visual Studio, so I hope it compiles ;)):
string[] lines = File.ReadAllLines("content.txt");
string[] separator = {":: "};
var splitOptions = StringSplitOptions.RemoveEmptyEntries;
var items = from line in lines
let parts = line.Split(separator, splitOptions)
group parts by parts[0] into partGroups
select partGroups;
A short explanation of the example above:
The result of the LINQ query is a IQueryable<IGrouping<string, IEnumberable<string>>>
.
Each item in the result has a Key
property containing the key of the line (title, description, note, ...).
Each item can be enumerated containing all of values.
Upvotes: 3
Reputation: 292765
You could use a Lookup<TKey, TElement>
:
ILookup<string, string> lookup = lines.Select(line => line.Split(new string[] { ":: " })
.ToLookup(arr => arr[0], arr => arr[1]);
IEnumerable<string> notes = lookup["note"];
Note that this collection is read-only
Upvotes: 2
Reputation: 69282
If your keys and values are strings then use a NameValueCollection. It supports multiple values for a given key.
It's not the most efficient collection in the world. Particularly because it's a non-generic class, uses a lot of virtual method calls, and the GetValues method will allocate arrays for its return values. But unless you require the best performing collection, this is certainly the most convenient collection that does what you ask.
Upvotes: 7
Reputation: 78292
I have used Dictionary<string, HashSet<string>>
for getting multiple values in the past. I would love to know if there is something better though.
Here is how you can emulate getting only one value.
public static bool TryGetValue(this Dictionary<string, HashSet<string>> map, string key, out string result)
{
var set = default(HashSet<string>);
if (map.TryGetValue(key, out set))
{
result = set.FirstOrDefault();
return result == default(string);
}
result = default(string);
return false;
}
Upvotes: 1
Reputation: 1933
I'm not a c# expert, but I think Dictionary<string, List<string>>
or some kind of HashMap<string, List<string>>
might work.
For example (Java pseudocode):
aKey aValue
aKey anotherValue
if(map.get(aKey) == null)
{
map.put(aKey, new ArrayList(){{add(aValue);}});
}
else
{
map.put(aKey, map.get(aKey).add(anotherValue));
}
or something similar. (or, the shortest way:
map.put(aKey, map.get(aKey) != null ? map.get(aKey).add(value) : new ArrayList(){{add(value);}});
Upvotes: 1