List.Contains(item) with generic list of objects - c#

If you have a List how do you return the item if a specified property or collection of properties exists?
public class Testing
{
public string value1 { get; set; }
public string value2 { get; set; }
public int value3 { get; set; }
}
public class TestingList
{
public void TestingNewList()
{
var testList = new List<Testing>
{
new Testing {value1 = "Value1 - 1", value2 = "Value2 - 1", value3 = 3},
new Testing {value1 = "Value1 - 2", value2 = "Value2 - 2", value3 = 2},
new Testing {value1 = "Value1 - 3", value2 = "Value2 - 3", value3 = 3},
new Testing {value1 = "Value1 - 4", value2 = "Value2 - 4", value3 = 4},
new Testing {value1 = "Value1 - 5", value2 = "Value2 - 5", value3 = 5},
new Testing {value1 = "Value1 - 6", value2 = "Value2 - 6", value3 = 6},
new Testing {value1 = "Value1 - 7", value2 = "Value2 - 7", value3 = 7}
};
//use testList.Contains to see if value3 = 3
//use testList.Contains to see if value3 = 2 and value1 = "Value1 - 2"
}
}

You could use
testList.Exists(x=>x.value3 == 3)

If you're using .NET 3.5 or better, LINQ is the answer to this one:
testList.Where(t => t.value3 == 3);
testList.Where(t => t.value3 == 2 && t.value1 == "Value1 - 2");
If not using .NET 3.5 then you can just loop through and pick out the ones you want.

Look at the Find or FindAll method of the List<T> class.

If you want to use the class's implementation of equality, you can use the Contains method. Depending on how you define equality (by default it'll be referential, which won't be any help), you may be able to run one of those tests. You could also create multiple IEqualityComparer<T>s for each test you want to perform.
Alternatively, for tests that don't rely just on the class's equality, you can use the Exists method and pass in a delegate to test against (or Find if you want a reference to the matching instance).
For example, you could define equality in the Testing class like so:
public class Testing: IEquatable<Testing>
{
// getters, setters, other code
...
public bool Equals(Testing other)
{
return other != null && other.value3 == this.value3;
}
}
Then you would test if the list contains an item with value3 == 3 with this code:
Testing testValue = new Testing();
testValue.value3 = 3;
return testList.Contains(testValue);
To use Exists, you could do the following (first with delegate, second with lambda):
return testList.Exists(delegate(testValue) { return testValue.value3 == 3 });
return testList.Exists(testValue => testValue.value3 == 2 && testValue.value1 == "Value1 - 2");

A LINQ query would probably be the easiest way to code this.
Testing result = (from t in testList where t.value3 == 3 select t).FirstOrDefault();

Related

C# Dynamic array

I want an array to contain strings, floats and ints that can be accessed via an index key.
I have an example in Lua how you would do but I don't know how you do it in C#
bookArray = [];
bookArray[1] =
{
Name = "Book 1";
Price = 50;
WPP = 374;
Pages = 42;
}
You may create a class and use List<MyClass>
class MyClass
{
public string Name {get;set;}
public double Price {get;set;}
public int Pages {get;set;}
}
Here is the list:
List<MyClass> values = new List<MyClass>();
Adding item
values.Add(new MyClass(){Name = "Book 1", Pages = 42, Price=50.0});
Insert at specific index:
values.Insert(0,new MyClass(){Name = "Book 2", Pages = 432, Price=10.0});
Retrieve at specific index:
MyClass theClass = values[1];
If you are ok with your inner type being immutable, you could do this:
var ar = new[] {
new { Name = "Book 1", Price = 50, WPP = 374, Pages = 42 },
new { Name = "Book 2", Price = 55, WPP = 220, Pages = 129 }
};
Which is about as close as you can come to the Lua definition

Linq GroupBy behaves differently for classes and values

Please consider the following code segment:
var list = new string[] { "ab", "ab", "cd", "cd", "cd" };
var groups = list.GroupBy(l => l);
var count = groups.Count();
The results:
count: 2,
groups: [{ Key: "ab", elements: ["ab", "ab"] }, { Key: "cd", elements: ["cd", "cd", "cd"] }]
When I do the same for class X:
public class X
{
public int A { get; set; }
public string B { get; set; }
}
And the same algorithm is used in order to create the grouped results:
var list2 = new X[]
{
new X { A = 1, B = "b1" },
new X { A = 1, B = "b1" },
new X { A = 2, B = "b2" },
new X { A = 2, B = "b2" },
new X { A = 2, B = "b2" },
};
var groups2 = list2.GroupBy(l => l);
var count2 = groups2.Count();
I would expect the same behavior. I would say count2 is 2, and groups2 contains the two different distinct data sets with 2 and 3 elements respectively.
However when I run this, I get 5 as count and a list of groups containing one item each. Why is the different behavior? I would expect the same aggregation algorithm to behave the same.
Thanks in advance for the explanation.
GroupBy uses default equality comparer for the type unless you provide any implementation.The default comparer for reference types only return true if they are same instances, meaning they have same references. If this is not the behaviour you want you have two choices:
Override Equals and GetHashCode methods in your clas
Implement an IEqualityComparer for your type and pass it to GroupBy

merge 2 complex lists in c#

I am programming in silverlight (c# .net)
lets say I have a list of type "data"
public class data
{
public string QUOTE_ID { get; set; }
public string EVENT_ACTION_CD { get; set; }
public string CUSTOMER_NAME { get; set; }
public string ADAPTIV_CODE { get; set; }
}
the problem is some of the data comes from 1 database and the other data comes from another, so right now i get the data in 2 steps - so i have something like this (using random numbers):
input1 = new List<data> //data return from database 1
//(the data is actually returned as a datable which i convert to a list
//to put to a datagrid, but the end result is shown below)
{
new data { QUOTE_ID = "1", EVENT_ACTION_CD = "2"},
new Project { QUOTE_ID = "2", EVENT_ACTION_CD = "4"},
new Project { QUOTE_ID = "3", EVENT_ACTION_CD = "5"}
};
input2 = new List<data> //data return from database 2
{
new data { QUOTE_ID = "1", CUSTOMER_NAME = "2", ADAPTIV_CODE ="5"},
new Project { QUOTE_ID = "2", CUSTOMER_NAME = "4", ADAPTIV_CODE = "5"},
new Project { QUOTE_ID = "3", CUSTOMER_NAME = "5", ADAPTIV_CODE = "7"}
};
so i should have 2 lists like
input1:
(1, 2, null, null
2, 4, null, null
3, 5, null, null)
and
input2:
(1, null, 2, 5
2, null, 4, 5
3. null, 5, 7)
how do i join them together to form one input list to become
(1, 2, 2, 5
2, 4, 4, 5
3, 5, 5, 7)
Use linq with a join operator.
See http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
var resultList = (from item in input1
join item2 in input2 on item2.QUOTE_ID equals input2.QUOTE_ID
let item.CUSTOMER_NAME = item2.CUSTOMER_NAME
let item.ADAPTIV_CODE = item2.ADAPTIV_CODE
select item).ToList();
A normal for loop would work for you:
for(int i = 0; i < input1.Count; i++){
if(input1[i].QUOTE_ID == null) input1[i].QUOTE_ID = input2[i].QUOTE_ID;
if(input1[i].EVENT_ACTION_CD == null) input1[i].EVENT_ACTION_CD = input2[i].EVENT_ACTION_CD;
if(input1[i].CUSTOMER_NAME == null) input1[i].CUSTOMER_NAME = input2[i].CUSTOMER_NAME;
if(input1[i].ADAPTIV_CODE == null) input1[i].ADAPTIV_CODE = input2[i].ADAPTIV_CODE;
}
The result will be saved into the input1. The code also supposes input1 and input2 have the same Count.
var input3 = input1.Join(
input2,
d1 => d1.QUOTE_ID,
d2 => d2.QUOTE_ID,
(d1, d2) => new data() {
QUOTE_ID = d1.QUOTE_ID,
EVENT_ACTION_CD = d1.EVENT_ACTION_CD,
CUSTOMER_NAME = d2.CUSTOMER_NAME,
ADAPTIV_CODE = d2.ADAPTIV_CODE
}
);

Ordering list by 3 numbers

I have a List<> with objects, that hold multiple fields, which are mostly numbers. I want to sort this list, by 3 of those numbers. I've tried this:
list = list.OrderBy(x => x.Val3).ThenBy(x => x.Val2)
.ThenBy(x => x.Val1).ToList();
which works fine, but only for the first two order/thenbys. The third one seems to not get run at all. I can sort for any combination of two of those values just fine, but the third on is always ignored.
I haven't tried the non LINQ approach yet, because I'm simply curious where the problem here is. Can't you sort for 3 values? What's the problem here? In case this matters in any way, 3 is a ushort, while 2 and 1 are uints.
I am not sure if there are any better solutions. If I were you, I would weight the value2 by multiply a big value. Just like:
list = list.OrderBy(x => x.Val3).ThenBy(x => x.Val2 * 10000 + x.Val1).ToList();
I think you can try this hack:
list = list.OrderBy(x => x.Val2).OrderBy(x => x.Val3).ThenBy(x => x.Val1).ToList();
This doesn't solve the problem because it just works. However, perhaps you can compare your code to this and see how it differs?
public class Foo
{
public string Name { get; set; }
public uint Val1 { get; set; }
public uint Val2 { get; set; }
public ushort Val3 { get; set; }
}
static void Main(string[] args)
{
OrderFoos();
Console.WriteLine("Press any key to end.");
Console.ReadKey();
}
private static void OrderFoos()
{
List<Foo> list = new List<Foo>();
list.Add(new Foo() { Name = "2nd", Val1 = 2, Val2 = 1, Val3 = 1 });
list.Add(new Foo() { Name = "1st", Val1 = 1, Val2 = 1, Val3 = 1 });
list.Add(new Foo() { Name = "3rd", Val1 = 1, Val2 = 2, Val3 = 1 });
list.Add(new Foo() { Name = "4th", Val1 = 2, Val2 = 1, Val3 = 2 });
list.Add(new Foo() { Name = "6th", Val1 = 4, Val2 = 1, Val3 = 3 });
list.Add(new Foo() { Name = "5th", Val1 = 3, Val2 = 1, Val3 = 3 });
list = list.OrderBy(x => x.Val3).ThenBy(x => x.Val2).ThenBy(x => x.Val1).ToList();
list.ForEach(x => Console.WriteLine(x.Name));
}
The documentation for ThenBy says
"This design enables you to specify multiple sort criteria by applying
any number of ThenBy or ThenByDescending methods."
So what you give as example code should work. In the absence of a specified comparator ThenBy uses the Default comparator. Are you sure that val1 is sortable using the default comparator?
EDIT: Doh! just saw "I can sort for any combination of two of those values just fine, but the third on is always ignored." so it seems that val1 is sortable by the default comparator.
The documentation and the answer from #Bob Horn suggests that your example should work. Can you add more details of the objects you're ordering?

Dictionary supporting duplicate, multidimensional keys?

I have a List<Thing> things, where a number of Things need to be frequently retrieved by looking up a combination of two variables T1 f1 and T2 f2, which are value types. They way I do that now is simply things.Where(t => t.Field1 == f1 && t.Field2 == f2). However, I do extremely many of those lookups frequently, and need a more effective method.
Fortunately, things does not need to have elements removed or added, so I thought of parsing the list on construction and add to a Dictionary<T1, Lookup<T2, Thing>>. However, this feels messy, especially with the added parsing. And it gets really hairy if I need to lookup even more fields. Three fields would look like Dictionary<T1, Dictionary<T2, Lookup<T3, Thing>>>.
My next thought was to make a Lookup<Tuple<T1,T2,T3,...>,Thing>. But in this case, I am not sure whether the keys will actually work because Tuple is a reference type.
Even if I make a Lookup<ValueType<T1,T2,T3,...>,Thing> things, the lookup statement will be something like things[new ValueType<T1,T2,T3,...>(f1, f2, f3, ...)] which is pretty ugly (and I am still not sure whether I could trust those keys).
Is there a more elegant solution to this which keeps the performance benefits of a hashtable and where I could simply type something like IEnumerable<Thing> found = things[f1, f2, f3, ...];?
Lookup<Tuple<T1,T2,T3,...>,Thing> will work, since Tuple overrides Equals and GetHashCode.
To make the lookup syntax less ugly, you can use Tuple.Create which supports type inference. Your code becomes things[Tuple.Create(f1, f2, f3, ...)]. If that's still too ugly, it's trivial to add a helper method that takes the individual values as parameters.
I'd also consider creating my own immutable class(or value type) for the key, so you get clean field names instead of ItemX. You just need to override Equals and GetHashCode consistently.
You can create multiple lookups, and then intersect them to do your searches. Here is a somewhat oversimplified example, but it should illustrate the idea:
class Test {
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
}
var list = new List<Test> {
new Test {A = "quick", B = "brown", C = "fox"}
, new Test {A = "jumps", B = "over", C = "the"}
, new Test {A = "lazy", B = "dog", C = "quick"}
, new Test {A = "brown", B = "fox", C = "jumps"}
, new Test {A = "over", B = "the", C = "lazy"}
, new Test {A = "dog", B = "quick", C = "brown"}
, new Test {A = "fox", B = "jumps", C = "over"}
, new Test {A = "the", B = "lazy", C = "dog"}
, new Test {A = "fox", B = "brown", C = "quick"}
, new Test {A = "the", B = "over", C = "jumps"}
, new Test {A = "quick", B = "dog", C = "lazy"}
, new Test {A = "jums", B = "fox", C = "brown"}
, new Test {A = "lazy", B = "the", C = "over"}
, new Test {A = "brown", B = "quick", C = "dog"}
, new Test {A = "over", B = "jumps", C = "fox"}
, new Test {A = "dog", B = "lazy", C = "the"}
};
var byA = list.ToLookup(v => v.A);
var byB = list.ToLookup(v => v.B);
var byC = list.ToLookup(v => v.C);
var all = byA["quick"].Intersect(byB["dog"]);
foreach (var test in all) {
Console.WriteLine("{0} {1} {2}", test.A, test.B, test.C);
}
all = byA["fox"].Intersect(byC["over"]);
foreach (var test in all) {
Console.WriteLine("{0} {1} {2}", test.A, test.B, test.C);
}
This prints
quick dog lazy
fox jumps over
Have you considered using a hash table with some kind of combination of the Fields as the key? I don't know enough about your data set to say if this is viable or not. Since the keys would need to be unique. But since you're not doing additions or removals using a hash table for look ups in memory is about as fast as you can get.
If i got you right, you can use Hashtable with Tuple, example below:
// populate Hastable
var hash = new Hashtable();
var tuple = Tuple.Create("string", 1, 1.0);
hash.Add(tuple,tuple);
// search for item you want
var anotherTuple = Tuple.Create("string", 1, 1.0);
// result will be tuple declared above
var result = hash[anotherTuple];
more complex solution (if duplicate keys needed):
public class Thing
{
public int Value1 { get; set; }
public double Value2 { get; set; }
public string Value3 { get; set; }
// preferable to create own Equals and GetHashCode methods
public Tuple<int, double> GetKey()
{
// create key on fields you want
return Tuple.Create(Value1, Value2);
}
}
usage
var t1 = new Thing() {Value1 = 1, Value2 = 1.0, Value3 = "something"};
var t2 = new Thing() {Value1 = 1, Value2 = 2.0, Value3 = "something"};
var hash = new [] { t1, t2 }.ToLookup(item => item.GetKey());
var criteria = new Thing() { Value1 = 1, Value2 = 2.0, value3 = "bla-bla-bla" };
var r = hash[criteria.GetKey()]; // will give you t1
The Linq Where or Dictionary of Dictionaries is probably the prettiest you are going to get. But it may be more of a question of how you are organising your data.
E.G. This never going to be a pretty way of accessing people data:
people["FirstName"]["LastName"]
It is usually better so try and come up with a simpler key.

Categories