Reputation: 85
C# code as below:
NameValueCollection nvc = new NameValueCollection();
nvc.Add("color", "red");
nvc.Add("size", "s");
nvc.Add("color", "red");
nvc.Add("size", "m");
nvc.Add("color", "red");
nvc.Add("size", "l");
//blue
nvc.Add("color", "blue");
nvc.Add("size", "m");
nvc.Add("color", "blue");
nvc.Add("size", "l");
//green
nvc.Add("color", "green");
nvc.Add("size", "s");
nvc.Add("color", "green");
nvc.Add("size", "m");
nvc.Add("color", "green");
nvc.Add("size", "l");
actual result:
color:red,red,red,blue,blue,green,green,green
size:s,m,l,m,l,s,m,l
hope result:
color:red,blue,green
size:s,m,l
Upvotes: 2
Views: 2062
Reputation: 39
Old question but I needed something similar(in VB.Net but easily applicable in C#); am using the collection to compile references between words/word groups and values while ignoring case. The dictionary solutions overwrite values, maintaining only the last value and a dictionary tied to a list becomes more plumbing maintenance than I like to bother with. To get exactly what is being asked, in a way that worked for me I first used the following syntax to add:
If Not nvc.AllKeys.Contains(name) OrElse Not nvc.GetValues(name).Contains(value) Then nvc.Add(name, value)
Large numbers of repeated calls to the same keys and values wont be very efficient but this is probably the simplest way I could find. This ended up being redundant and in my code name(and sometimes even value)was being built ad-hoc; in order to reduce code/variables and increase efficiency as much as I could(still using this somewhat inefficient method), I first considered building an <Extension()> Sub but instead created this:
Private Class NoDupValueNameValueCollection
Inherits Collections.Specialized.NameValueCollection
Public Overrides Sub Add(name As String, value As String)
If Not MyBase.AllKeys.Contains(name) OrElse Not MyBase.GetValues(name).Contains(value) Then MyBase.Add(name, value)
End Sub
End Class
or C#
private class NoDupValueNameValueCollection : System.Collections.Specialized.NameValueCollection {
public override void Add(string name, string value) {
if (!(base.AllKeys.Contains(name)) || !(base.GetValues(name).Contains(value))) base.Add(name, value);
}
}
It can now be used in place of and exactly as nvc and will prevent duplicate values. In fact it is exactly what is being asked: a NameValueCollection that prevents the addition of duplicate values
Upvotes: 0
Reputation: 50271
Some thoughts for you:
NameValueCollection
allows duplicate values. Use a Dictionary<string, string>
.NameValueCollection
is an older object that is strongly typed to string, string
only. Dictionary
is more flexible, because you can specify the type parameters.When using Dictionary
note that you will get errors if you try to add the same key twice. This can be remedied by changing your syntax slightly. Instead of Add()
, just use the indexer:
var myDict = new Dictionary<string, string>();
myDict["color"] = "blue";
myDict["color"] = "blue"; // no harm doing this twice
In my experience, using strings all over the place is a way to let bugs creep in, and makes it easy to make mistakes (hmmm, wonder how I know this?). It's too easy to misspell something, or assign a size to a color or vice versa.
Is there a reason you can't strongly type the concepts in your code?
public abstract class ProductAttribute { }
public sealed class Color : ProductAttribute {
private Color(string value) { Value = value; }
public Value { get; }
public static readonly Blue = new Color("blue");
public static readonly Green = new Color("green");
private static readonly IReadOnlyDictionary<string, Color> _colorDict =
new ReadOnlyDictionary<string, Color>(
new List<Color> {
Blue, Green
}.ToDictionary(color => color.Value, color => color)
);
public static Color GetColor(string value) {
Color color;
_colorDict.TryGetValue(value, out color);
return color;
}
}
Then do something similar for sizes. This is just one idea, perhaps not very flexible (you have to change the code to add new values), but it should show you what's possible.
But look what you can do now:
var attrs = new HashSet<ProductAttribute>;
attrs.Add(Color.Blue);
attrs.Add(Color.Green);
attrs.Add(Color.Blue); // no problem
attrs.Add(Color.GetColor("blue")); // no problem
attrs.Add(Size.Small);
attrs.Add(Size.Medium);
attrs.Add(Size.Medium); // no problem
One way to work with the ProductAttribute
classes is like so:
foreach (ProductAttribute attr in attrs) {
Color color = attr as Color;
if (color != null) {
// work with color
}
}
Alternately, you can create a ProductAttributeType
enum, add it as a parameter in the ProductAttribute
constructor and expose it in a read-only abstract property in the ProductAttribute
class. Then all your subclasses pass in what type they are, and you can easily get the type of something just doing attr.ProductAttributeType == ProductAttributeType.Color
for example.
Upvotes: 2
Reputation: 16956
I'm not sure on your use case, NameValueCollection
allows duplicates better to use Dictionary
for such cases.
Still you could do something like this on NameValueCollection
to get the results you want.
var results = nvc.AllKeys
.GroupBy(g=>g)
.Select(s=>
new
{
key = s.Key,
values = nvc.GetValues(s.Key).Distinct()
});
foreach(var result in results)
{
Console.WriteLine("{0}- {1}", result.key, string.Join(",", result.values) ;
}
Working Example
Upvotes: 1