What's the best way to create a list with an arbitrary number of instances of the same object? i.e is there a more compact or efficient way to do the following?
static List<MyObj> MyObjs = Enumerable.Range(0, 100)
.Select(i => new MyObj())
.ToList();
(Enumerable.Repeat would give me ten references to the same object, so I don't think it would work.)
Edited to reflect that this method does not work.
I was curious about your comment about Enumerable.Repeat, so I tried it.
//do not use!
List<object> myList = Enumerable.Repeat(new object(), 100).ToList();
I confirmed that they do all share the same reference like the OP mentioned.
This wouldn't be hard to implement as an iterator:
IEnumerable<T> CreateItems<T> (int count) where T : new() {
return CreateItems(count, () => new T());
}
IEnumerable<T> CreateItems<T> (int count, Func<T> creator) {
for (int i = 0; i < count; i++) {
yield return creator();
}
}
Apparently, the answer is "no". Thanks, everyone!
Not sure what is wrong with a for loop in this case. At the very least, we can presize the capacity of the list. That might not be important for 100 objects, but the size is arbitrary.
public class MyClass
{
static int Capacity = 100;
static List<MyObj> MyObjs = new List<MyObj>(Capacity);
static MyClass() {
for( var i = 0; i < Capacity; i++ ) {
MyObjs.Add(new MyObj());
}
}
}
you can use the enumerable as a base and use select to create the new objects:
List<object> myList = Enumerable.Repeat(null, 100).Select(_ => new object()).ToList();
you can attach a debugger, the new is executed every time.
This is almost the same as your code, but you don't have to provide a fake range.
You only provide a null as fake object you want to repeat...
Related
At present, I'm using something like this to build a list of 10 objects:
myList = (from _ in Enumerable.Range(0, 10) select new MyObject {...}).toList()
This is based off my python background, where I'd write:
myList = [MyObject(...) for _ in range(10)]
Note that I want my list to contain 10 instances of my object, not the same instance 10 times.
Is this still a sensible way to do things in C#? Is there a cost to doing it this way over a simple for loop?
Fluent API looks a little more readable in this case, but its not very easy to see the intent of your code:
var list = Enumerable.Range(0, 10).Select(_ => new MyObject()).ToList();
Simple if loop is fast and easy to understand, but it also hides intent - creating list of 10 items
List<MyObject> list = new List<MyObject>();
for (int i = 0; i < 10; i++)
list.Add(new MyObject());
The best thing for readability is a builder, which will describe your intent
public class Builder<T>
where T : new()
{
public static IList<T> CreateListOfSize(int size)
{
List<T> list = new List<T>();
for (int i = 0; i < size; i++)
list.Add(new T());
return list;
}
}
Usage:
var list = Builder<MyObject>.CreateListOfSize(10);
This solution is as fast, as simple loop, and intent is very clear. Also in this case we have minimum amount of code to write.
You can try:
Enumerable.Range(0, 1000).Select(x => new MyObject()).ToArray();
Personally, I think Enumerable.Repeat is missing an overload. A handy addition would be something like this:
public static class EnumerableEx
{
public static IEnumerable<T> Repeat<T>(int amt, Func<T> producer)
{
for(var i = 0; i < amt; ++i)
{
yield return producer();
}
}
}
so you could
EnumerableEx.Repeat(10, () => new object()) //.ToList()
I'm attempting to use Enumerable.OrderBy to sort a List because ultimately I want to be able to sort by more than a single field. At the moment it only appears to work if I create a new variable var to hold the results view which means (I think) the types need to be re-cast.
Is there a method to sort a List by more than 1 field whilst retaining the original List variable and types? I.e. I'd rather end up with variable _orderedbins of type List<orderedbins>
Below is what I currently have but everything from var test = ... onwards seems a bit wrong.
public class orderedBins
{
public string Bin { get; set; }
public int Order { get; set; }
}
List<orderedbins> _orderedbins = new List<orderedbins>();
foreach (string item in splitbins)
{
string[] spbinSetting = item.Split(',');
bool bchecked = bool.Parse(spbinSetting[1]);
int border = int.Parse(spbinSetting[2]);
if (bchecked == true)
{
_orderedbins.Add(new orderedbins { bin = spbinSetting[0], Order = border });
}
}
var test =_orderedbins.OrderBy(x => x.Order);
foreach (var item in test)
{
string f = item.Bin;
int g = item.Order;
}
You know, you can perform multiple sub-sorts for an order by...
lst.OrderBy(x => x.Prop1).ThenBy(x => x.Prop2).ThenByDescending(x => x.Prop3)...
Just add a .ToList(); and introduce it with a variable, to have the result in a list variable.
EDIT:
Great suggestion by Willem, for more readability:
from x in lst
order by x.Prop1, x.Prop2, x.Prop3
select x
You can create a new sorted list without creating a new variable using
list = list.OrderBy(item => item.Field1).ThenBy(item => item.Field1).ToList();
It will still create an entirely new list though (it's not actually much of a problem to add a new variable; those are cheap. Creating a new list, doing this, is fine as long as the list isn't very large.
If you need to sort the list in place then you'll want to use a custom comparer with the List's sort method:
public class MyComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
if(x.Field1 != y.Field1)
return x.Field1.CompareTo(y.Field1)
else
return x.Field2.CompareTo(y.Field2);
}
}
List<MyClass> list = new List<MyClass>();
//Populate list
list.Sort(new MyComparer());
As others suggested, using Linq's OrderBy(...).ToList() would be a cleaner way, but this will give you a new instance of the list.
To retain the original instance, consider to use List<T>.Sort():
_orderedbins.Sort(new Comparison<orderedBins>((obj1, obj2) =>
{
int result = obj1.Order.CompareTo(obj2.Order);
return result != 0 ? result : obj1.Bin.CompareTo(obj2.Bin);
}));
This will do the trick:
_orderedbins = _orderedbins.OrderBy(x => x.Order).ToList();
...but there's no real issue creating a new variable/reference.
I think this will do it (it's already a list of orderbins so no casting is required):
_orderbins = _orderbins.OrderBy(x => x.Order).ToList();
I am trying to figure out a way to correctly sort a bunch of different arraylists.
I am publishing content articles and every value [0] in an arraylist will relate to every other value [0]. and so on. Each element makes up the collective parts of a complete content item.
Now, the last element, popularity, is the amount of clicks an item has received. How do I
do a sort of the content items based on popularity without mixing up the html for each article?
*EDIT I am limited by the .NET 2.0 Framework at Work*
Below is the code... thanks.
public class MultiDimDictList : Dictionary<string, ArrayList> { }
myDicList.Add("fly", a_fly);
myDicList.Add("img", a_img);
myDicList.Add("bar", a_bar);
myDicList.Add("meter", a_meter);
myDicList.Add("block", a_block);
myDicList.Add("popularity", a_pop);
If you use the following code you can convert your existing dictionary of arraylists into a collection of Dictionaries and thus allowing a simple sort using Linq OrderBy
// Get the shortest arraylist length (they should be equal this is just a paranoia check!)
var count=myDicList.Values.Min(x=>x.Count);
// Get the collection of Keys
var keys=myDicList.Keys;
// Perform the conversion
var result=Enumerable.Range(0,count).Select(i=>keys.Select(k=>new {Key=k,Value=myDicList[k][i]}).ToDictionary(x=>x.Key,x=>x.Value));
var sorted=result.OrderByDescending(x=>x["popularity"]).ToList()
-- EDIT VERSION FOR .NET 2.0
First you need a comparer class
class PopularityComparison : IComparer<Dictionary<string,object>> {
private bool _sortAscending;
public PopularityComparison(bool sortAscending) {
_sortAscending = sortAscending;
}
public int Compare(Dictionary<string, object> x, Dictionary<string, object> y) {
object xValue = x["popularity"];
object yValue = y["popularity"];
// Sort Ascending
if (_sortAscending) {
return Comparer.Default.Compare(xValue, yValue);
} else {
return Comparer.Default.Compare(yValue, xValue);
}
}
}
Then you can use the following code
// Get the shortest arraylist length (they should be equal this is just a paranoia check!)
// Replacement for min
int count = int.MaxValue;
foreach (ArrayList a in myDicList.Values) if (a.Count < count) count = a.Count;
// Get the collection of Keys
Dictionary<string, ArrayList>.KeyCollection keys = myDicList.Keys;
// Perform the conversion
List<Dictionary<string, object>> result = new List<Dictionary<string, object>>(count);
for (int i = 0; i < count; i++) {
Dictionary<string, object> row = new Dictionary<string, object>(keys.Count);
foreach (string key in keys) row.Add(key, myDicList[key][i]);
result.Add(row);
}
And then finally to sort in ascending popularity order
result.Sort(new PopularityComparison(true));
or Descending order
result.Sort(new PopularityComparison(true));
I'd think it would be better to have an object containing your keys as properties, then a single collection with each item you'd have in your array lists.
This way you'd have a single collection sort, which becomes trivial if using Linq.OrderBy().
something like...
public class Article
{
public string Fly{get;set;}
public string Img{get;set;}
// etc.
public float Popularity{get;set;}
}
Then...
List<Article> articles = ... get from somewhere, or convert from your array lists.
List<Article> sorted = articles.OrderBy(a=>a.Popularity).ToList();
Please excuse the napkin code here... I'll update it if you need more detail.
An example using non-linq.
Create an implementation of IComparer.
public class ArticleComparer : IComparer<Article>
{
public bool Accending { get; set; }
public int Compare(Article x, Article y)
{
float result = x.Popularity - y.Popularity;
if (!Accending) { result *= -1; }
if (result == 0) { return 0; }
if (result > 0) return 1;
return -1;
}
}
Then when you go to sort the List, you can do something like the following.
ArticleComparer comparer = new ArticleComparer();
comparer.Accending = false;
articles.Sort(comparer);
This would be much easier if you had a list of article objects, each of which contained properties for fly, img, bar, popularity, etc. But if you really have to store things using this inside-out approach, then the only way you can sort the content items based on popularity is to create another array (or list) to hold the order.
Create a new list and populate it with sequential indexes:
List<int> OrderedByPopularity = new List<int>();
ArrayList popList = myDicList["popularity"];
for (int i = 0; i < popList.Count; ++i)
{
OrderedByPopularity.Add(i);
}
Now you have a list that contains the indexes of the items in the popularity list. Now you can sort:
OrderedByPopularity.Sort((i1, i2) => return popList[i1].CompareTo(popList[i2]););
But that gives you the least popular article first. If you want to reverse the sort so that OrderedByPopularity[0] is the most popular item:
OrderedByPopularity.Sort((i1, i2) => { return popList[i2].CompareTo(popList[i1]);});
Really, though, you should look into restructuring your application. It's much easier to work with objects that have properties rather than trying to maintain parallel arrays of properties.
If you have to do this in .NET 2.0, declare the poplist array at class scope (rather than method scope), and create a comparison method.
ArrayList poplist;
void MyMethod()
{
List<int> OrderedByPopularity = new List<int>();
popList = myDicList["popularity"];
for (int i = 0; i < popList.Count; ++i)
{
OrderedByPopularity.Add(i);
}
OrderedByPopularity.Sort(PopularityComparison);
// ...
}
int PopularityComparison(int i1, int i2)
{
return ((int)popList[i2]).CompareTo((int)popList[i1]);
}
I'm looking for a performant way to add distinct items of a second ICollection to an existing one. I'm using .NET 4.
This should do it:
list1.Union(list2).Distinct(aCustomComparer).ToList()
As long as they're IEnumerable, you can use the go-to Linq answer:
var union = firstCollection.Union(secondCollection);
This will use the default equality comparison, which for most objects is referential equality. To change this, you can define an IEqualityComparer generic to the item type in your collection that will perform a more semantic comparison, and specify it as the second argument of the Union.
Another way to add to your exisiting list would be:
list1.AddRange(list2.Distinct().Except(list1));
The most direct answer to your question - since you didn't give much detail on the actual types of ICollection you have as input or need as output is the one given by KeithS
var union = firstCollection.Union(secondCollection);
This will return a distinct IEnumerable - if that is what you need then it is VERY fast. I made a small test app (below) that ran the union method (MethodA) against a simple hashset method of deduplicating and returns a Hashset<>(MethodB). The union method DESTROYS the hashset:
MethodA: 1ms
MethodB: 2827ms
However -- Having to convert that IEnumerable to some other type of collection such as List<> (like the version ADas posted) changes everything:
Simply adding .ToList() to MethodA
var union = firstCollection.Union(secondCollection).ToList();
Changes the results:
MethodA: 3656ms
MethodB: 2803ms
So - it seems more would need to be known about the specific case you are working with - and any solution you come up with should be tested - since a small (code) change can have HUGE impacts.
Below is the test I used to compare these methods - I'm sure it is a stupid way to test - but it seems to work :)
private static void Main(string[] args)
{
ICollection<string> collectionA = new List<string>();
ICollection<string> collectionB = new List<string>();
for (int i = 0; i < 1000; i++)
{
string randomString = Path.GetRandomFileName();
collectionA.Add(randomString);
collectionA.Add(randomString);
collectionB.Add(randomString);
collectionB.Add(randomString);
}
Stopwatch testA = new Stopwatch();
testA.Start();
MethodA(collectionA, collectionB);
testA.Stop();
Stopwatch testB = new Stopwatch();
testB.Start();
MethodB(collectionA, collectionB);
testB.Stop();
Console.WriteLine("MethodA: {0}ms", testA.ElapsedMilliseconds);
Console.WriteLine("MethodB: {0}ms", testB.ElapsedMilliseconds);
Console.ReadLine();
}
private static void MethodA(ICollection<string> collectionA, ICollection<string> collectionB)
{
for (int i = 0; i < 10000; i++)
{
var result = collectionA.Union(collectionB);
}
}
private static void MethodB(ICollection<string> collectionA, ICollection<string> collectionB)
{
for (int i = 0; i < 10000; i++)
{
var result = new HashSet<string>(collectionA);
foreach (string s in collectionB)
{
result.Add(s);
}
}
}
I m not on .NET 4.
I get a huge list from a data source. When the number of elements in the list are higher than X i like to partition the list, assign each partition to a thread. after processing partitions i like to merge them.
var subsets = list.PartitionEager(50000);
//var subsets = list.Partition<T>(50000);
Thread[] threads = new Thread[subsets.Count()];
int i = 0;
foreach (var set in subsets)
{
threads[i] = new Thread(() => Convertor<T>(set));
threads[i].Start();
i++;
}
for (int j = 0; j < i; j++)
{
threads[j].Join();
}
Convertor method is a static method that takes a list and does some lookup.
public static void Convertor<T>(List<T> list) where T : IInterface {
foreach (var element in list)
{
**// do some lookup and assing a value to element
// then do more lookup and assign a value to element**
}
}
When i run this code, even though i know that most of the elements will be assigned a value. They are in fact coming back null.
I m aware that the copy of the list will be passed to the method but any change to the element should be reflected in the upper method. however this is happening only within the final subset.
I even added some code to merge the lists into a single one.
list.Clear();
foreach (var set in subsets)
{
list.AddRange(set);
}
code for paritioning:
public static List<List<T>> PartitionEager<T>(this List<T> source, Int32 size)
{
List<List<T>> merged = new List<List<T>>();
for (int i = 0; i < Math.Ceiling(source.Count / (Double)size); i++)
{
merged.Add(new List<T>(source.Skip(size * i).Take(size)));
}
return merged;
}
What am i doing wrong? how to resolve this issue? i d like the elements to be assigned values after the lookups? is this related to synchronization or parameter passing?
If .NET 4 is an option, you can just use Parallel.For or Parallel.ForEach. These methods automatically handle partitioning for you, as well as providing many other advantages in terms of scalability across multiple degrees of concurrency on different systems.
Looks like you're having modified closure while creating threads. If I'm correct then all your threads update the same (last) set. Modify the code in this way:
foreach (var set in subsets)
{
var setLocalCopy = set;
threads[i] = new Thread(() => Convertor<T>(setLocalCopy));
threads[i].Start();
i++;
}