lets say I have a custom class:
public class WineCellar
{
public string year;
public string wine;
public double nrbottles;
}
Lets say I now have a List of this custom class:
List<WineCellar> orignialwinecellar = List<WineCellar>();
containing these items:
2012 Chianti 12
2011 Chianti 6
2012 Chardonay 12
2011 Chardonay 6
I know that if I want to compare two list and return a new list that has only items that are not in the other list I would do:
var newlist = list1.Except(list2);
How can I extend this to a custom class? Lets say I have:
string[] exceptionwinelist = {"Chardonay", "Riesling"};
I would like this to be returned:
List<WineCellar> result = originalwinecellar.wine.Except(exceptionwinelist);
This pseudocode obviously doesnt work but hopefully illustrates what I m trying to do. This shoudl then return a List of the custom class winecellar with following items:
2012 Chianti 12
2011 Chianti 6
Thanks.
You don't really want to use Except here, as you don't have a collection of WineCellar objects to use as a blacklist. What you have is a collection of rules: "I don't want objects with such and such wine names".
Therefore it's better to simply use Where:
List<WineCellar> result = originalwinecellar
.Where(w => !exceptionwinelist.Contains(w.wine))
.ToList();
In human-readable form:
I want all WineCellars where the wine name is not present in the list of exceptions.
As an aside, the WineCellar class name is a bit misleading; those objects are not cellars, they are inventory items.
One solution is with an extension method:
public static class WineCellarExtensions
{
public static IEnumerable<WineCellar> Except(this List<WineCellar> cellar, IEnumerable<string> wines)
{
foreach (var wineCellar in cellar)
{
if (!wines.Contains(wineCellar.wine))
{
yield return wineCellar;
}
}
}
}
And then use it like this:
List<WineCellar> result = originalwinecellar.Except(exceptionwinelist).ToList();
exceptionWineList is a string[] but originalWineCellar is a List<WineCellar>, WineCellar is not a string, so it does not make sense to perform an Except between these.
You could just as easily do,
// use HashSet for look up performance.
var exceptionWineSet = new HashSet<string>(exceptionWineList);
var result = orginalWineCellar.Where(w => !exceptionWineSet.Contains(w.Wine));
What I think you are alluding to in your question is something like
WineCellar : IEquatable<string>
{
...
public bool Equals(string other)
{
return other.Equals(this.wine, StringComparison.Ordinal);
}
}
which allows you to equate WineCellars to strings.
However, if I were to rework your model I'd come up with something like,
enum WineColour
{
Red,
White,
Rose
}
enum WineRegion
{
Bordeaux,
Rioja,
Alsace,
...
}
enum GrapeVariety
{
Cabernet Sauvignon,
Merlot,
Ugni Blanc,
Carmenere,
...
}
class Wine
{
public string Name { get; set; }
public string Vineyard { get; set; }
public WineColour Colour { get; set; }
public WineRegion Region { get; set; }
public GrapeVariety Variety { get; set; }
}
class WineBottle
{
public Wine Contents { get; set; }
public int Millilitres { get; set; }
public int? vintage { get; set; }
}
class Bin : WineBottle
{
int Number { get; set; }
int Quantity { get; set; }
}
class Cellar : ICollection<WineBottle>
{
...
}
Then, you can see that there are several ways to compare Wine and I may want to filter a Cellar on one or more of Wine's properties. Therefore I might be temtpted to give myself some flexibility,
class WineComparer : EqualityComparer<Wine>
{
[Flags]
public Enum WineComparison
{
Name = 1,
Vineyard= 2,
Colour = 4,
Region = 8,
Variety = 16,
All = 31
}
private readonly WineComparison comparison;
public WineComparer()
: this WineComparer(WineComparison.All)
{
}
public WineComparer(WineComparison comparison)
{
this.comparison = comparison;
}
public override bool Equals(Wine x, Wine y)
{
if ((this.comparison & WineComparison.Name) != 0
&& !x.Name.Equals(y.Name))
{
return false;
}
if ((this.comparison & WineComparison.Vineyard) != 0
&& !x.Vineyard.Equals(y.Vineyard))
{
return false;
}
if ((this.comparison & WineComparison.Region) != 0
&& !x.Region.Equals(y.Region))
{
return false;
}
if ((this.comparison & WineComparison.Colour) != 0
&& !x.Colour.Equals(y.Colour))
{
return false;
}
if ((this.comparison & WineComparison.Variety) != 0
&& !x.Variety.Equals(y.Variety))
{
return false;
}
return true;
}
public override bool GetHashCode(Wine obj)
{
var code = 0;
if ((this.comparison & WineComparison.Name) != 0)
{
code = obj.Name.GetHashCode();
}
if ((this.comparison & WineComparison.Vineyard) != 0)
{
code = (code * 17) + obj.Vineyard.GetHashCode();
}
if ((this.comparison & WineComparison.Region) != 0)
{
code = (code * 17) + obj.Region.GetHashCode();
}
if ((this.comparison & WineComparison.Colour) != 0)
{
code = (code * 17) + obj.Colour.GetHashCode();
}
if ((this.comparison & WineComparison.Variety) != 0)
{
code = (code * 17) + obj.Variety.GetHashCode();
}
return code;
}
}
this probably looks like a lot of effort but it has some use. Lets say we wanted all the wine except the Red Rioja in your cellar, you could do something like,
var comparison = new WineComparer(
WineComparison.Colour + WineComparison.Region);
var exception = new Wine { Colour = WineColour.Red, Region = WineRegion.Rioja };
var allButRedRioja = cellar.Where(c =>
!comparison.Equals(c.Wine, exception));
I had this exact same issue to. I tried the example from Darren but couldn't get that to work properly.
I therefore made a modification from DarrenĀ“s example as follows:
static class Helper
{
public static IEnumerable<Product> Except(this List<Product> x, List<Product> y)
{
foreach(var xi in x)
{
bool found = false;
foreach (var yi in y) { if(xi.Name == yi.Name) { found = true; } }
if(!found) { yield return xi; }
}
}
}
This works for me. You can possibly add several fields in the if clause if needed.
To directly use such extension methods with generic classes you should implement comparator. It consists of two methods: Equal and GetHashCode. You should implement them in your WineCellar class.
Note the second example.
Note that the hash-based methods are much faster than basic 'List.Contains...' implementations.
Related
I'm coding at C# and I'm trying to make an OOP representation of a list of topics. I tried countless approaches but I still not being able to reach the desired result.
I want to make a method later that will output it like:
1) Text
1.1) Text
2) Text
2.1) Text
2.2) Text
2.2.1) Text
2.2.2) Text
2.3) Text
3) Text
3.1) Text
When needed to get a single topic, I would like to create a method calling my object like:
private string GetSingleTopic()
{
return $"{Topic.Numerator}) {Topic.Text}"
}
EXAMPLES
Example 1
I would be able to instantiate the object such as:
var variable = new TopicObject
{
"TitleA",
"TitleB",
"TitleC"
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
--- OUTPUT --- */
Example 2
Be able to instantiate the object such as:
var variable = new TopicObject
{
"TitleA",
"TitleB",
"TitleC":
{
"TitleD":
{
"TitleE"
},
"TitleF":
{
"TitleG",
"TitleH"
}
}
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
3.1) TitleD
3.1.2) TitleE
3.2) TitleF
3.2.1) TitleG
3.2.2) TitleH
--- OUTPUT --- */
My Approach
This, was one of my many approaches. I couldn't use it because I can't initialize the inner topic List in the way i mentioned, like an hierarchy.
But the structure is pretty similar to what I want to achieve so I decided to put here as an example.
public abstract class TopicBase
{
public List<Topic> Topics { get; set; } // optional
protected TopicBase() { Topics = new List<Topic>(); }
protected TopicBase(List<Topic> topics) { Topics = topics; }
public TopicBase AddTopic(string topicText)
{
var test = new Topic(topicText);
Topics.Add(test);
return this;
}
}
public class Topic
{
public Topic(string text)
{
Numerator++;
Text = text;
}
public int Numerator { get; }
public string Text { get; }
}
public class TopicLevel1 : TopicBase { }
public class TopicLevel2 : TopicBase { }
public class TopicLevel3 : TopicBase { }
Let's start by defining a data structure that can hold the topics:
public class Topics<T> : List<Topic<T>> { }
public class Topic<T> : List<Topic<T>>
{
public T Value { get; private set; }
public Topic(T value, params Topic<T>[] children)
{
this.Value = value;
if (children != null)
this.AddRange(children);
}
}
That allows us to write this code:
var topics = new Topics<string>()
{
new Topic<string>("TitleA"),
new Topic<string>("TitleB"),
new Topic<string>("TitleC",
new Topic<string>("TitleD",
new Topic<string>("TitleE")),
new Topic<string>("TitleF",
new Topic<string>("TitleF"),
new Topic<string>("TitleH")))
};
That matches your "Example 2" data.
To output the result we add two ToOutput methods.
To Topics<T>:
public IEnumerable<string> ToOutput(Func<T, string> format)
=> this.SelectMany((t, n) => t.ToOutput(0, $"{n + 1}", format));
To Topic<T>:
public IEnumerable<string> ToOutput(int depth, string prefix, Func<T, string> format)
{
yield return $"{new string(' ', depth)}{prefix}) {format(this.Value)}";
foreach (var child in this.SelectMany((t, n) => t.ToOutput(depth + 1, $"{prefix}.{n + 1}", format)))
{
yield return child;
}
}
Now I can run this code:
foreach (var line in topics.ToOutput(x => x))
{
Console.WriteLine(line);
}
That gives me:
1) TitleA
2) TitleB
3) TitleC
3.1) TitleD
3.1.1) TitleE
3.2) TitleF
3.2.1) TitleF
3.2.2) TitleH
If the goal is to have some structure that will help with the output of the topic hierarchy, you already have it (and may even be able to simplify it more).
For example, here's an almost-minimal Topic to get what you want:
public class Topic
{
public string Title { get; set; }
public List<Topic> SubTopics { get; private set; } = new();
public Topic() : this("DocRoot") { }
public Topic(string title) => Title = title;
public void AddTopics(List<Topic> subTopics) => SubTopics.AddRange(subTopics);
public void AddTopics(params Topic[] subTopics) => SubTopics.AddRange(subTopics);
public override string ToString() => Title;
}
That is, you have a Topic that can have SubTopics (aka children) and that's all you need.
With that, we can build your second example:
var firstLevelTopics = new List<Topic>();
for (var c = 'A'; c < 'D'; ++c)
{
firstLevelTopics.Add(new Topic(c.ToString()));
}
var cTopic = firstLevelTopics.Last();
cTopic.AddTopics(
new Topic
{
Title = "D",
SubTopics = { new Topic("E") }
},
new Topic
{
Title = "F",
SubTopics = { new Topic("G"), new Topic("H") }
});
Now, imagine if we had a function to print the hierarchy from the list of top-level topics. I'm leaving the final detail for yourself in case this is homework.
PrintTopics(firstLevelTopics);
static void PrintTopics(List<Topic> topics, string prefix = "")
{
// For the simple case, we can just loop and print...
for (var i = 0; i < topics.Count; ++i)
{
var topic = topics[i];
var level = i + 1;
Console.WriteLine($"{prefix}{level}) {topic}");
// ...but, if we want to print the children, we need more.
// Make a recursive call to print the SubTopics
// PrintTopics(<What goes here?>);
}
}
I have a large file which, in essence contains data like:
Netherlands,Noord-holland,Amsterdam,FooStreet,1,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,2,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,3,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,4,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,5,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,1,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,2,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,3,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,4,...,...
Netherlands,Noord-holland,Amstelveen,BazDrive,1,...,...
Netherlands,Noord-holland,Amstelveen,BazDrive,2,...,...
Netherlands,Noord-holland,Amstelveen,BazDrive,3,...,...
Netherlands,Zuid-holland,Rotterdam,LoremAve,1,...,...
Netherlands,Zuid-holland,Rotterdam,LoremAve,2,...,...
Netherlands,Zuid-holland,Rotterdam,LoremAve,3,...,...
...
This is a multi-gigabyte file. I have a class that reads this file and exposes these lines (records) as an IEnumerable<MyObject>. This MyObject has several properties (Country,Province,City, ...) etc.
As you can see there is a LOT of duplication of data. I want to keep exposing the underlying data as an IEnumerable<MyObject>. However, some other class might (and probably will) make some hierarchical view/structure of this data like:
Netherlands
Noord-holland
Amsterdam
FooStreet [1, 2, 3, 4, 5]
BarRoad [1, 2, 3, 4]
...
Amstelveen
BazDrive [1, 2, 3]
...
...
Zuid-holland
Rotterdam
LoremAve [1, 2, 3]
...
...
...
...
When reading this file, I do, essentially, this:
foreach (line in myfile) {
fields = line.split(",");
yield return new MyObject {
Country = fields[0],
Province = fields[1],
City = fields[2],
Street = fields[3],
//...other fields
};
}
Now, to the actual question at hand: I could use string.Intern() to intern the Country, Province, City, and Street strings (those are the main 'vilains', the MyObject has several other properties not relevant to the question).
foreach (line in myfile) {
fields = line.split(",");
yield return new MyObject {
Country = string.Intern(fields[0]),
Province = string.Intern(fields[1]),
City = string.Intern(fields[2]),
Street = string.Intern(fields[3]),
//...other fields
};
}
This will save about 42% of memory (tested and measured) when holding the entire dataset in memory since all duplicate strings will be a reference to the same string. Also, when creating the hierarchical structure with a lot of LINQ's .ToDictionary() method the keys (Country, Province etc.) of the resp. dictionaries will be much more efficient.
However, one of the drawbacks (aside a slight loss of performance, which is not problem) of using string.Intern() is that the strings won't be garbage collected anymore. But when I'm done with my data I do want all that stuff garbage collected (eventually).
I could use a Dictionary<string, string> to 'intern' this data but I don't like the "overhead" of having a key and value where I am, actually, only interested in the key. I could set the value to null or the use the same string as value (which will result in the same reference in key and value). It's only a small price of a few bytes to pay, but it's still a price.
Something like a HashSet<string> makes more sense to me. However, I cannot get a reference to a string in the HashSet; I can see if the HashSet contains a specific string, but not get a reference to that specific instance of the located string in the HashSet. I could implement my own HashSet for this, but I am wondering what other solutions you kind StackOverflowers may come up with.
Requirements:
My "FileReader" class needs to keep exposing an IEnumerable<MyObject>
My "FileReader" class may do stuff (like string.Intern()) to optimize memory usage
The MyObject class cannot change; I won't make a City class, Country class etc. and have MyObject expose those as properties instead of simple string properties
Goal is to be (more) memory efficient by de-duplicating most of the duplicate strings in Country, Province, City etc.; how this is achieved (e.g. string interning, internal hashset / collection / structure of something) is not important. However:
I know I can stuff the data in a database or use other solutions in such direction; I am not interested in these kind of solutions.
Speed is only of secondary concern; the quicker the better ofcourse but a (slight) loss in performance while reading/iterating the objects is no problem
Since this is a long-running process (as in: windows service running 24/7/365) that, occasionally, processes a bulk of this data I want the data to be garbage-collected when I'm done with it; string interning works great but will, in the long run, result in a huge string pool with lots of unused data
I would like any solutions to be "simple"; adding 15 classes with P/Invokes and inline assembly (exaggerated) is not worth the effort. Code maintainability is high on my list.
This is more of a 'theoretical' question; it's purely out of curiosity / interest that I'm asking. There is no "real" problem, but I can see that in similar situations this might be a problem to someone.
For example: I could do something like this:
public class StringInterningObject
{
private HashSet<string> _items;
public StringInterningObject()
{
_items = new HashSet<string>();
}
public string Add(string value)
{
if (_items.Add(value))
return value; //New item added; return value since it wasn't in the HashSet
//MEH... this will quickly go O(n)
return _items.First(i => i.Equals(value)); //Find (and return) actual item from the HashSet and return it
}
}
But with a large set of (to be de-duplicated) strings this will quickly bog down. I could have a peek at the reference source for HashSet or Dictionary or... and build a similar class that doesn't return bool for the Add() method but the actual string found in the internals/bucket.
The best I could come up with until now is something like:
public class StringInterningObject
{
private ConcurrentDictionary<string, string> _items;
public StringInterningObject()
{
_items = new ConcurrentDictionary<string, string>();
}
public string Add(string value)
{
return _items.AddOrUpdate(value, value, (v, i) => i);
}
}
Which has the "penalty" of having a Key and a Value where I'm actually only interested in the Key. Just a few bytes though, small price to pay. Coincidally this also yields 42% less memory usage; the same result as when using string.Intern() yields.
tolanj came up with System.Xml.NameTable:
public class StringInterningObject
{
private System.Xml.NameTable nt = new System.Xml.NameTable();
public string Add(string value)
{
return nt.Add(value);
}
}
(I removed the lock and string.Empty check (the latter since the NameTable already does that))
xanatos came up with a CachingEqualityComparer:
public class StringInterningObject
{
private class CachingEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public System.WeakReference X { get; private set; }
public System.WeakReference Y { get; private set; }
private readonly IEqualityComparer<T> Comparer;
public CachingEqualityComparer()
{
Comparer = EqualityComparer<T>.Default;
}
public CachingEqualityComparer(IEqualityComparer<T> comparer)
{
Comparer = comparer;
}
public bool Equals(T x, T y)
{
bool result = Comparer.Equals(x, y);
if (result)
{
X = new System.WeakReference(x);
Y = new System.WeakReference(y);
}
return result;
}
public int GetHashCode(T obj)
{
return Comparer.GetHashCode(obj);
}
public T Other(T one)
{
if (object.ReferenceEquals(one, null))
{
return null;
}
object x = X.Target;
object y = Y.Target;
if (x != null && y != null)
{
if (object.ReferenceEquals(one, x))
{
return (T)y;
}
else if (object.ReferenceEquals(one, y))
{
return (T)x;
}
}
return one;
}
}
private CachingEqualityComparer<string> _cmp;
private HashSet<string> _hs;
public StringInterningObject()
{
_cmp = new CachingEqualityComparer<string>();
_hs = new HashSet<string>(_cmp);
}
public string Add(string item)
{
if (!_hs.Add(item))
item = _cmp.Other(item);
return item;
}
}
(Modified slightly to "fit" my "Add() interface")
As per Henk Holterman's request:
public class StringInterningObject
{
private Dictionary<string, string> _items;
public StringInterningObject()
{
_items = new Dictionary<string, string>();
}
public string Add(string value)
{
string result;
if (!_items.TryGetValue(value, out result))
{
_items.Add(value, value);
return value;
}
return result;
}
}
I'm just wondering if there's maybe a neater/better/cooler way to 'solve' my (not so much of an actual) problem. By now I have enough options I guess
Here are some numbers I came up with for some simple, short, preliminary tests:
Non optimizedMemory: ~4,5GbLoad time: ~52s
StringInterningObject (see above, the ConcurrentDictionary variant)Memory: ~2,6GbLoad time: ~49s
string.Intern()Memory: ~2,3GbLoad time: ~45s
System.Xml.NameTableMemory: ~2,3GbLoad time: ~41s
CachingEqualityComparerMemory: ~2,3GbLoad time: ~58s
StringInterningObject (see above, the (non-concurrent) Dictionary variant) as per Henk Holterman's request:Memory: ~2,3GbLoad time: ~39s
Although the numbers aren't very definitive, it seems that the many memory-allocations for the non-optimized version actually slow down more than using either string.Intern() or the above StringInterningObjects which results in (slightly) longer load times. Also, string.Intern() seems to 'win' from StringInterningObject but not by a large margin; << See updates.
I've had exactly this requirement and indeed asked on SO, but with nothing like the detail of your question, no useful responses. One option that is built in is a (System.Xml).NameTable, which is basically a string atomization object, which is what you are looking for, we had (we've actually move to Intern because we do keep these strings for App-life).
if (name == null) return null;
if (name == "") return string.Empty;
lock (m_nameTable)
{
return m_nameTable.Add(name);
}
on a private NameTable
http://referencesource.microsoft.com/#System.Xml/System/Xml/NameTable.cs,c71b9d3a7bc2d2af shows its implemented as a Simple hashtable, ie only storing one reference per string.
Downside? is its completely string specific. If you do cross-test for memory / speed I'd be interested to see the results. We were already using System.Xml heavily, might of course not seem so natural if you where not.
When in doubt, cheat! :-)
public class CachingEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public T X { get; private set; }
public T Y { get; private set; }
public IEqualityComparer<T> DefaultComparer = EqualityComparer<T>.Default;
public bool Equals(T x, T y)
{
bool result = DefaultComparer.Equals(x, y);
if (result)
{
X = x;
Y = y;
}
return result;
}
public int GetHashCode(T obj)
{
return DefaultComparer.GetHashCode(obj);
}
public T Other(T one)
{
if (object.ReferenceEquals(one, X))
{
return Y;
}
if (object.ReferenceEquals(one, Y))
{
return X;
}
throw new ArgumentException("one");
}
public void Reset()
{
X = default(T);
Y = default(T);
}
}
Example of use:
var comparer = new CachingEqualityComparer<string>();
var hs = new HashSet<string>(comparer);
string str = "Hello";
string st1 = str.Substring(2);
hs.Add(st1);
string st2 = str.Substring(2);
// st1 and st2 are distinct strings!
if (object.ReferenceEquals(st1, st2))
{
throw new Exception();
}
comparer.Reset();
if (hs.Contains(st2))
{
string cached = comparer.Other(st2);
Console.WriteLine("Found!");
// cached is st1
if (!object.ReferenceEquals(cached, st1))
{
throw new Exception();
}
}
I've created an equality comparer that "caches" the last Equal terms it analyzed :-)
Everything could then be encapsulated in a subclass of HashSet<T>
/// <summary>
/// An HashSet<T;gt; that, thorough a clever use of an internal
/// comparer, can have a AddOrGet and a TryGet
/// </summary>
/// <typeparam name="T"></typeparam>
public class HashSetEx<T> : HashSet<T> where T : class
{
public HashSetEx()
: base(new CachingEqualityComparer<T>())
{
}
public HashSetEx(IEqualityComparer<T> comparer)
: base(new CachingEqualityComparer<T>(comparer))
{
}
public T AddOrGet(T item)
{
if (!Add(item))
{
var comparer = (CachingEqualityComparer<T>)Comparer;
item = comparer.Other(item);
}
return item;
}
public bool TryGet(T item, out T item2)
{
if (Contains(item))
{
var comparer = (CachingEqualityComparer<T>)Comparer;
item2 = comparer.Other(item);
return true;
}
item2 = default(T);
return false;
}
private class CachingEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public WeakReference X { get; private set; }
public WeakReference Y { get; private set; }
private readonly IEqualityComparer<T> Comparer;
public CachingEqualityComparer()
{
Comparer = EqualityComparer<T>.Default;
}
public CachingEqualityComparer(IEqualityComparer<T> comparer)
{
Comparer = comparer;
}
public bool Equals(T x, T y)
{
bool result = Comparer.Equals(x, y);
if (result)
{
X = new WeakReference(x);
Y = new WeakReference(y);
}
return result;
}
public int GetHashCode(T obj)
{
return Comparer.GetHashCode(obj);
}
public T Other(T one)
{
if (object.ReferenceEquals(one, null))
{
return null;
}
object x = X.Target;
object y = Y.Target;
if (x != null && y != null)
{
if (object.ReferenceEquals(one, x))
{
return (T)y;
}
else if (object.ReferenceEquals(one, y))
{
return (T)x;
}
}
return one;
}
}
}
Note the use of WeakReference so that there aren't useless references to objects that could prevent garbage collection.
Example of use:
var hs = new HashSetEx<string>();
string str = "Hello";
string st1 = str.Substring(2);
hs.Add(st1);
string st2 = str.Substring(2);
// st1 and st2 are distinct strings!
if (object.ReferenceEquals(st1, st2))
{
throw new Exception();
}
string stFinal = hs.AddOrGet(st2);
if (!object.ReferenceEquals(stFinal, st1))
{
throw new Exception();
}
string stFinal2;
bool result = hs.TryGet(st1, out stFinal2);
if (!object.ReferenceEquals(stFinal2, st1))
{
throw new Exception();
}
if (!result)
{
throw new Exception();
}
edit3:
instead of indexing strings, putting them in non-duplicate lists will save much more ram.
we have int indexes in class MyObjectOptimized. access is instant.
if list is short(like 1000 item) speed of setting values wont be noticable.
i assumed every string will have 5 character .
this will reduce memory usage
percentage : 110 byte /16byte = 9x gain
total : 5gb/9 = 0.7 gb + sizeof(Country_li , Province_li etc )
with int16 index (will further halve ram usage )
*note:* int16 capacity is -32768 to +32767 ,
make sure your list is not bigger than 32 767
usage is same but will use the class MyObjectOptimized
main()
{
// you can use same code
foreach (line in myfile) {
fields = line.split(",");
yield
return
new MyObjectOptimized {
Country = fields[0],
Province = fields[1],
City = fields[2],
Street = fields[3],
//...other fields
};
}
}
required classes
// single string size : 18 bytes (empty string size) + 2 bytes per char allocated
//1 class instance ram cost : 4 * (18 + 2* charCount )
// ie charcounts are at least 5
// cost: 4*(18+2*5) = 110 byte
class MyObject
{
string Country ;
string Province ;
string City ;
string Street ;
}
public static class Exts
{
public static int AddDistinct_and_GetIndex(this List<string> list ,string value)
{
if( !list.Contains(value) ) {
list.Add(value);
}
return list.IndexOf(value);
}
}
// 1 class instance ram cost : 4*4 byte = 16 byte
class MyObjectOptimized
{
//those int's could be int16 depends on your distinct item counts
int Country_index ;
int Province_index ;
int City_index ;
int Street_index ;
// manuallly implemented properties will not increase memory size
// whereas field WILL increase
public string Country{
get {return Country_li[Country_index]; }
set { Country_index = Country_li.AddDistinct_and_GetIndex(value); }
}
public string Province{
get {return Province_li[Province_index]; }
set { Province_index = Province_li.AddDistinct_and_GetIndex(value); }
}
public string City{
get {return City_li[City_index]; }
set { City_index = City_li.AddDistinct_and_GetIndex(value); }
}
public string Street{
get {return Street_li[Street_index]; }
set { Street_index = Street_li.AddDistinct_and_GetIndex(value); }
}
//beware they are static.
static List<string> Country_li ;
static List<string> Province_li ;
static List<string> City_li ;
static List<string> Street_li ;
}
I have a class(KeywordProperties) with this code :
public class KeywordProperties
{
[DisplayMode("0-1,0-2,0-3,1-1,1-2,1-3,1-6,1-9,1-10,1-11,1-12,2-1,2-2,2-3,2-9,2-10,2-12,3-1,3-2,3-3,3-10,3-12,4-13,5,6")]
public string Onvaan { get; set; }
[DisplayMode("0-1,0-2,0-3,1-1,1-2,1-3,1-6,1-9,1-10,1-11,1-12,2-1,2-2,2-3,2-9,2-10,2-12,3-1,3-2,3-3,3-10,3-12,4-13,5,6")]
public string MozooKolli { get; set; }
[DisplayMode("0-10,1-10,3-10,3-12,5,6")]
public string EsmeDars { get; set; }
[DisplayMode("0-1,1-1,2-1,2-2,3-1,6")]
public string Sokhanraan { get; set; }
[DisplayMode("0-10,1-2,2-1,2-10,3-10,6")]
public string Modares { get; set; }
}
And I have another for check attributes :
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DisplayModeAttribute : Attribute
{
private readonly string mode;
public DisplayModeAttribute(string mode)
{
this.mode = mode ?? "";
}
public override bool Match(object obj)
{
var other = obj as DisplayModeAttribute;
if (other == null) return false;
if (other.mode == mode) return true;
// allow for a comma-separated match, in either direction
if (mode.IndexOf(',') >= 0)
{
string[] tokens = mode.Split(',');
if (Array.IndexOf(tokens, other.mode) >= 0) return true;
}
else if (other.mode.IndexOf(',') >= 0)
{
string[] tokens = other.mode.Split(',');
if (Array.IndexOf(tokens, mode) >= 0) return true;
}
return false;
}
}
I want display properties in propertygrid with this code :
String Code = "":
KeywordProperties Kp = new KeywordProperties();
propertygrid1.SelectedObject = Kp;
propertygrid1.BrowsableAttributes = new AttributeCollection(new DisplayModeAttribute(Code));
When Code vlue is "0-1" or "5" or ...(single value), I can see my properties.
But, when use "0-1,1-2" for Code, I can't see any thing in my properygrid.
How can I see these data :
1- All properties that have code 0-1 and code 1-2 :
result is :Onvaan,MozooKolli
2- All properties that have code 0-1 or code 1-2 :
result is : Onvaan,MozooKolli,Sokhanraan,Modares
It appears that your code only matches DisplayModeAttributes when both have a single value, or one contains a single value and the other contains multiple values; it won't match them when both contain multiple values, unless the list of values are identical.
To use your code as-is, you could change the way you populate PropertyGrid.BrowsableAttributes:
propertygrid1.BrowsableAttributes = new AttributeCollection(
new DisplayModeAttribute("0-1"),
new DisplayModeAttribute("1-2")
// etc.
);
Alternatively, to fix your matching code, you could replace it with something like:
public override bool Match(object obj)
{
var other = obj as DisplayModeAttribute;
if (other == null)
return false;
if (other.mode == mode)
return true;
string[] modes = mode.Split(',');
string[] others = other.mode.Split(',');
var matches = modes.Intersect(others);
return matches.Count() > 0;
}
This uses the LINQ Intersect method, which returns the elements that two lists have in common.
Question:
Can anyone tell me why my unit test is failing with this error message?
CollectionAssert.AreEquivalent failed. The expected collection contains 1
occurrence(s) of . The actual
collection contains 0 occurrence(s).
Goal:
I'd like to check if two lists are identical. They are identical if both contain the same elements with the same property values. The order is irrelevant.
Code example:
This is the code which produces the error. list1 and list2 are identical, i.e. a copy-paste of each other.
[TestMethod]
public void TestListOfT()
{
var list1 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
var list2 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
}
public class MyPerson
{
public string Name { get; set; }
public int Age { get; set; }
}
I've also tried this line (source)
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
and this line (source)
CollectionAssert.AreEquivalent(list1.ToArray(), list2.ToArray());
P.S.
Related Stack Overflow questions:
I've seen both these questions, but the answers didn't help.
CollectionAssert use with generics?
Unit-testing IList with CollectionAssert
You are absolutely right. Unless you provide something like an IEqualityComparer<MyPerson> or implement MyPerson.Equals(), the two MyPerson objects will be compared with object.Equals, just like any other object. Since the objects are different, the Assert will fail.
It works if I add an IEqualityComparer<T> as described on MSDN and if I use Enumerable.SequenceEqual. Note however, that now the order of the elements is relevant.
In the unit test
//CollectionAssert.AreEquivalent(list1, list2); // Does not work
Assert.IsTrue(list1.SequenceEqual(list2, new MyPersonEqualityComparer())); // Works
IEqualityComparer
public class MyPersonEqualityComparer : IEqualityComparer<MyPerson>
{
public bool Equals(MyPerson x, MyPerson y)
{
if (object.ReferenceEquals(x, y)) return true;
if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) return false;
return x.Name == y.Name && x.Age == y.Age;
}
public int GetHashCode(MyPerson obj)
{
if (object.ReferenceEquals(obj, null)) return 0;
int hashCodeName = obj.Name == null ? 0 : obj.Name.GetHashCode();
int hasCodeAge = obj.Age.GetHashCode();
return hashCodeName ^ hasCodeAge;
}
}
I was getting this same error when testing a collection persisted by nHibernate. I was able to get this to work by overriding both the Equals and GetHashCode methods. If I didn't override both I still got the same error you mentioned:
CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of .
The actual collection contains 0 occurrence(s).
I had the following object:
public class EVProjectLedger
{
public virtual long Id { get; protected set; }
public virtual string ProjId { get; set; }
public virtual string Ledger { get; set; }
public virtual AccountRule AccountRule { get; set; }
public virtual int AccountLength { get; set; }
public virtual string AccountSubstrMethod { get; set; }
private Iesi.Collections.Generic.ISet<Contract> myContracts = new HashedSet<Contract>();
public virtual Iesi.Collections.Generic.ISet<Contract> Contracts
{
get { return myContracts; }
set { myContracts = value; }
}
public override bool Equals(object obj)
{
EVProjectLedger evProjectLedger = (EVProjectLedger)obj;
return ProjId == evProjectLedger.ProjId && Ledger == evProjectLedger.Ledger;
}
public override int GetHashCode()
{
return new { ProjId, Ledger }.GetHashCode();
}
}
Which I tested using the following:
using (ITransaction tx = session.BeginTransaction())
{
var evProject = session.Get<EVProject>("C0G");
CollectionAssert.AreEquivalent(TestData._evProjectLedgers.ToList(), evProject.EVProjectLedgers.ToList());
tx.Commit();
}
I'm using nHibernate which encourages overriding these methods anyways. The one drawback I can see is that my Equals method is based on the business key of the object and therefore tests equality using the business key and no other fields. You could override Equals however you want but beware of equality pollution mentioned in this post:
CollectionAssert.AreEquivalent failing... can't figure out why
If you would like to achieve this without having to write an equality comaparer, there is a unit testing library that you can use, called FluentAssertions,
https://fluentassertions.com/documentation/
It has many built in equality extension functions including ones for the Collections. You can install it through Nuget and its really easy to use.
Taking the example in the question above all you have to write in the end is
list1.Should().BeEquivalentTo(list2);
By default, the order matters in the two collections, however it can be changed as well.
I wrote this to test collections where the order is not important:
public static bool AreCollectionsEquivalent<T>(ICollection<T> collectionA, ICollection<T> collectionB, IEqualityComparer<T> comparer)
{
if (collectionA.Count != collectionB.Count)
return false;
foreach (var a in collectionA)
{
if (!collectionB.Any(b => comparer.Equals(a, b)))
return false;
}
return true;
}
Not as elegant as using SequenceEquals, but it works.
Of course to use it you simply do:
Assert.IsTrue(AreCollectionsEquivalent<MyType>(collectionA, collectionB, comparer));
public class CarSpecs
{
public String CarName { get; set; }
public String CarMaker { get; set; }
public DateTime CreationDate { get; set; }
}
This is a list and I am trying to figure out an efficient way to sort this list List CarList, containing 6(or any integer amount) Cars, by the Car Make Date. I was going to do Bubble sort, but will that work? Any Help?
Thanks
The List<T> class makes this trivial for you, since it contains a Sort method. (It uses the QuickSort algorithm, not Bubble Sort, which is typically better anyway.) Even better, it has an overload that takes a Comparison<T> argument, which means you can pass a lambda expression and make things very simple indeed.
Try this:
CarList.Sort((x, y) => DateTime.Compare(x.CreationDate, y.CreationDate));
You could use LINQ:
listOfCars.OrderBy(x => x.CreationDate);
EDIT: With this approach, its easy to add on more sort columns:
listOfCars.OrderBy(x => x.CreationDate).ThenBy(x => x.Make).ThenBy(x => x.Whatever);
The best approach is to implement either IComparable or IComparable<T>, and then call List<T>.Sort(). This will do all the hard work of sorting for you.
Another option would be to use a custom comparer:
using System;
using System.Collections.Generic;
using System.Text;
namespace Yournamespace
{
class CarNameComparer : IComparer<Car>
{
#region IComparer<Car> Members
public int Compare(Car car1, Car car2)
{
int returnValue = 1;
if (car1 != null && car2 == null)
{
returnValue = 0;
}
else if (car1 == null && car2 != null)
{
returnValue = 0;
}
else if (car1 != null && car2 != null)
{
if (car1.CreationDate.Equals(car2.CreationDate))
{
returnValue = car1.Name.CompareTo(car2.Name);
}
else
{
returnValue = car2.CreationDate.CompareTo(car1.CreationDate);
}
}
return returnValue;
}
#endregion
}
}
which you call like this:
yourCarlist.Sort(new CarNameComparer());
Note: I didn't compile this code so you might have to remove typo's
Edit: modified it so the comparer compares on creationdate as requested in question.
I would just use the build in List.Sort method. It uses the QuickSort algorithm which on average runs in O(n log n).
This code should work for you, I change your properties to auto-properties, and defined a static CompareCarSpecs method that just uses the already existing DateTime.CompareTo method.
class Program
{
static void Main(string[] args)
{
List<CarSpecs> cars = new List<CarSpecs>();
cars.Sort(CarSpecs.CompareCarSpecs);
}
}
public class CarSpecs
{
public string CarName { get; set; }
public string CarMaker { get; set; }
public DateTime CreationDate { get; set; }
public static int CompareCarSpecs(CarSpecs x, CarSpecs y)
{
return x.CreationDate.CompareTo(y.CreationDate);
}
}
Hope this helps.
Putting some of the pieces mentioned here together. This compiles and works in C# 4.x and VS2010. I tested with a WinForm. So add the method to the WinForm Main(). You will need the System.Linq and System.Generic.Collections assemblies at least.
private void SortCars()
{
List<CarSpecs> cars = new List<CarSpecs>();
List<CarSpecs> carsSorted = new List<CarSpecs>();
cars.Add(new CarSpecs
{
CarName = "Y50",
CarMaker = "Ford",
CreationDate = new DateTime(2011, 4, 1),
});
cars.Add(new CarSpecs
{
CarName = "X25",
CarMaker = "Volvo",
CreationDate = new DateTime(2012, 3, 1),
});
cars.Add(new CarSpecs
{
CarName = "Z75",
CarMaker = "Datsun",
CreationDate = new DateTime(2010, 5, 1),
});
//More Comprehensive if needed
//cars.OrderBy(x => x.CreationDate).ThenBy(x => x.CarMaker).ThenBy(x => x.CarName);
carsSorted.AddRange(cars.OrderBy(x => x.CreationDate));
foreach (CarSpecs caritm in carsSorted)
{
MessageBox.Show("Name: " +caritm.CarName
+ "\r\nMaker: " +caritm.CarMaker
+ "\r\nCreationDate: " +caritm.CreationDate);
}
}
}
public class CarSpecs
{
public string CarName { get; set; }
public string CarMaker { get; set; }
public DateTime CreationDate { get; set; }
}
If you're after an efficient way of sorting, I'd advise against using bubble sort and go for a quick sort instead. This page provides a rather good explanation of the algorithm:
http://www.devhood.com/Tutorials/tutorial_details.aspx?tutorial_id=574
Best of luck!
I would avoid writing my own sorting algorithm, but if you are going to anyway, have a look at http://www.sorting-algorithms.com/ for some comparrisons of different sorting algorithms...
If you are using 2.0, the following discussion may be useful: C# List<> Sort by x then y
If you use delegates (also known as anonymous methods), you won't have to implement any IComparer / IComparable interfaces.
public static void Main(string[] args)
{
List<CarSpecs> list = new List<CarSpecs>();
list.Add(new CarSpecs("Focus", "Ford", new DateTime(2010,1, 2));
list.Add(new CarSpecs("Prius", "Toyota", new DateTime(2012,3, 3));
list.Add(new CarSpecs("Ram", "Dodge", new DateTime(2013, 10, 6));
list.Sort(delegate (CarSpecs first, CarSpecs second)
{
int returnValue = 1;
if((first != null & second != null))
{
if (first.CarName.Equals(second.CarName))
{
if (first.CarMaker.Equals(second.CarMaker))
{
returnValue = first.CreationDate.CompareTo(second.CreationDate);
}
else
{
returnValue = first.CarMaker.CompareTo(second.CarMaker);
}
}
else
{
returnValue = first.CarName.CompareTo(second.CarName);
}
}
return returnValue;
});
}
To extend the answer of Noldorin, in order to sort a list with int datatype this can be used:
listName.Sort((x, y) => x.CompareTo(y));
Or if you have a complex object in the list:
inventoryList.Sort((x, y) => x.stockNumber.CompareTo(y.stockNumber));