How to use dictionary in c# to compare two lists - c#

Currently, I have implemented two lists with a double for loop to find matches between the two lists so I can join on them.
I have a list A which contains an ID and some other columns. I have a list B which contains an ID and some other columns. I have currently implemented a for loop within a for loop in order to make the comparisons for all the IDs so that I can find the ones that match and then return the joined results. I know want to understand how to implement a dictionary in this case as that will be more efficient to fix this problem.
public IEnumerable<Details> GetDetails(string ID)
{
// there are two lists defined up here
for (var item in listA)
{
for (var item2 in listB)
{
if (item.ID == item2.ID)
{
item.Name = item2.name;
}
}
}
return results;
}
Instead of having this double for loop, which is very inefficient. I want to learn how to implement a dictionary to fix this problem.

The dictionary would use the ids as keys (or indexes) so
Dictionary<string, object> myListA = new Dictionary<string, object>();
Dictionary<string, object> myListB = new Dictionary<string, object>();
public object GetDetails(string ID)
{
object a = myListA[ID];
object b = myListB[ID];
// combine them here how you want
// object c = a + b;
return c;
}

How about using linq to achieve your actual requirement? Something like:
public IEnumerable<A> GetDetails(int ID)
{
var listA = new List<A>
{
new A(){ ID = 1, Name = 2 },
new A(){ ID = 3, Name = 4 },
new A(){ ID = 5, Name = 6 },
};
var listB = new List<B>
{
new B(){ X = 1, name = 0 },
new B(){ X = 3, name = 1 }
};
return listA.Join(listB, k => k.ID, k => k.ID, (item, item2) =>
{
item.Name = item2.name;
return item;
}).Where(w => w.ID == ID);
}

If you just want the common IDs in the two lists, you can achieve that like this:
var commonIds = listA.Select(o => o.ID).Intersect(listB.Select(o => o.ID));

Related

c# how to create an array of an anonymous type? [duplicate]

In C# 3.0 you can create anonymous class with the following syntax
var o = new { Id = 1, Name = "Foo" };
Is there a way to add these anonymous class to a generic list?
Example:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<var> list = new List<var>();
list.Add(o);
list.Add(o1);
Another Example:
List<var> list = new List<var>();
while (....)
{
....
list.Add(new {Id = x, Name = y});
....
}
You could do:
var list = new[] { o, o1 }.ToList();
There are lots of ways of skinning this cat, but basically they'll all use type inference somewhere - which means you've got to be calling a generic method (possibly as an extension method). Another example might be:
public static List<T> CreateList<T>(params T[] elements)
{
return new List<T>(elements);
}
var list = CreateList(o, o1);
You get the idea :)
Here is the answer.
string result = String.Empty;
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
foreach (var item in list)
{
result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}
MessageBox.Show(result);
There are many ways to do this, but some of the responses here are creating a list that contains garbage elements, which requires you to clear the list.
If you are looking for an empty list of the generic type, use a Select against a List of Tuples to make the empty list. No elements will be instantiated.
Here's the one-liner to create an empty list:
var emptyList = new List<Tuple<int, string>>()
.Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();
Then you can add to it using your generic type:
emptyList.Add(new { Id = 1, Name = "foo" });
emptyList.Add(new { Id = 2, Name = "bar" });
As an alternative, you can do something like below to create the empty list (But, I prefer the first example because you can use it for a populated collection of Tuples as well) :
var emptyList = new List<object>()
.Select(t => new { Id = default(int), Name = default(string) }).ToList();
Not exactly, but you can say List<object> and things will work. However, list[0].Id won't work.
This will work at runtime in C# 4.0 by having a List<dynamic>, that is you won't get IntelliSense.
If you are using C# 7 or above, you can use tuple types instead of anonymous types.
var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
I guess
List<T> CreateEmptyGenericList<T>(T example) {
return new List<T>();
}
void something() {
var o = new { Id = 1, Name = "foo" };
var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}
will work.
You might also consider writing it like this:
void something() {
var String = string.Emtpy;
var Integer = int.MinValue;
var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
I usually use the following; mainly because you then "start" with a list that's empty.
var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.
Lately, I've been writing it like this instead:
var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
Using the repeat method would also allow you to do:
var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });
..which gives you the initial list with the first item already added.
You can do this in your code.
var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
I checked the IL on several answers. This code efficiently provides an empty List:
using System.Linq;
…
var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
In latest version 4.0, can use dynamic like below
var list = new List<dynamic>();
list.Add(new {
Name = "Damith"
});
foreach(var item in list){
Console.WriteLine(item.Name);
}
}
You can create a list of dynamic.
List<dynamic> anons=new List<dynamic>();
foreach (Model model in models)
{
var anon= new
{
Id = model.Id,
Name=model.Name
};
anons.Add(anon);
}
"dynamic" gets initialized by the first value added.
Here is a another method of creating a List of anonymous types that allows you to start with an empty list, but still have access to IntelliSense.
var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();
If you wanted to keep the first item, just put one letter in the string.
var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Here is my attempt.
List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }};
I came up with this when I wrote something similar for making a Anonymous List for a custom type.
I'm very surprised nobody has suggested collection initializers. This way can only add objects when the list is created hence the name however it seems like the nicest way of doing it. No need to create an array then convert it to a list.
var list = new List<dynamic>()
{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
};
You can always use object instead of dynamic but trying to keep it in a true generic way then dynamic makes more sense.
Instead of this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List <var> list = new List<var>();
list.Add(o);
list.Add(o1);
You could do this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<object> list = new List<object>();
list.Add(o);
list.Add(o1);
However, you will get a compiletime error if you try to do something like this in another scope, although it works at runtime:
private List<object> GetList()
{
List<object> list = new List<object>();
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
list.Add(o);
list.Add(o1);
return list;
}
private void WriteList()
{
foreach (var item in GetList())
{
Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine);
}
}
The problem is that only the members of Object are available at runtime, although intellisense will show the properties id and name.
In .net 4.0 a solution is to use the keyword dynamic istead of object in the code above.
Another solution is to use reflection to get the properties
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var anonymous = p.GetList(new[]{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
});
p.WriteList(anonymous);
}
private List<T> GetList<T>(params T[] elements)
{
var a = TypeGenerator(elements);
return a;
}
public static List<T> TypeGenerator<T>(T[] at)
{
return new List<T>(at);
}
private void WriteList<T>(List<T> elements)
{
PropertyInfo[] pi = typeof(T).GetProperties();
foreach (var el in elements)
{
foreach (var p in pi)
{
Console.WriteLine("{0}", p.GetValue(el, null));
}
}
Console.ReadLine();
}
}
}
You can do it this way:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
var array = new[] { o, o1 };
var list = array.ToList();
list.Add(new { Id = 3, Name = "Yeah" });
It seems a little "hacky" to me, but it works - if you really need to have a list and can't just use the anonymous array.
This is an old question, but I thought I'd put in my C# 6 answer. I often have to set up test data that is easily entered in-code as a list of tuples. With a couple of extension functions, it is possible to have this nice, compact format, without repeating the names on each entry.
var people= new List<Tuple<int, int, string>>() {
{1, 11, "Adam"},
{2, 22, "Bill"},
{3, 33, "Carol"}
}.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });
This gives an IEnumerable - if you want a list that you can add to then just add ToList().
The magic comes from custom extension Add methods for tuples, as described at https://stackoverflow.com/a/27455822/4536527.
public static class TupleListExtensions {
public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
T1 item1, T2 item2) {
list.Add(Tuple.Create(item1, item2));
}
public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
T1 item1, T2 item2, T3 item3) {
list.Add(Tuple.Create(item1, item2, item3));
}
// and so on...
}
The only thing I don't like is that the types are separated from the names, but if you really don't want to make a new class then this approach will still let you have readable data.
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
For your second example, where you have to initialize a new List<T>, one idea is to create an anonymous list, and then clear it.
var list = new[] { o, o1 }.ToList();
list.Clear();
//and you can keep adding.
while (....)
{
....
list.Add(new { Id = x, Name = y });
....
}
Or as an extension method, should be easier:
public static List<T> GetEmptyListOfThisType<T>(this T item)
{
return new List<T>();
}
//so you can call:
var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();
Or probably even shorter,
var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
Deriving from this answer, I came up with two methods that could do the task:
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
/// for the needed type inference. This overload is for when you don't have an instance of the anon class
/// and don't want to make one to make the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(Func<T> definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
/// only used for the needed type inference. This overload is for when you do have an instance of the anon
/// class and don't want the compiler to waste time making a temp class to define the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(T definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
You can use the methods like
var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
//or
var existingAnonInstance = new { Id = 59, Name = "Joe" };
var otherEmptyList = CreateListOfAnonType(existingAnonInstance);
This answer has a similar idea, but I didn't see it until after I made those methods.
Using Reflection
Microsoft documentation about this topic.
using System;
using System.Collections;
using System.Collections.Generic;
var anonObj = new { Id = 1, Name = "Foo" };
var anonType = anonObj.GetType();
var listType = typeof(List<>);
// We know that List<> have only one generic argument, so we do this:
var contructed = listType.MakeGenericType(anonType);
// Create instance
var instance = Activator.CreateInstance(contructed);
// Using it
var list = (IList)instance;
list.Add(anonObj);
For Dictionary<,> you need to pass 2 arguments
Ex.: dicType.MakeGenericType( type1, type2 )
And for generic types with constraints (where T : struct),
we need to do more verifications. Check microsoft docs to learn how.
Try with this:
var result = new List<object>();
foreach (var test in model.ToList()) {
result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}

Is there one liner LINQ construct for creating dictionary by merging two lists and removing duplicates

I have two classes, and two collections of each:
public class ClassA {
String key;
String value;
// Some other fields specific to ClassA
}
public class ClassB {
String key;
String value;
// Some other fields specific to ClassB
}
List<ClassA> listA;
List<ClassB> listB;
I want to build Dictionary<String, String> from key and value from listA and listB where value would be from listB if both lists contain object with same key but different values.
Now, I have a solution - create dictionaryA from listA, create dictionaryB from listB, merge those dictionaries by properly handling duplicates. There are several SO posts explaining how to do just that.
My question is bit more academic - Is there one liner LINQ construct that can do what I want to do?
Here is one way:
var merged =
listA.Select(b => new { key = b.key, val = b.value })
.Union(listB.Select(b => new { key = b.key, val = b.value }))
.ToDictionary(m => m.key, n => n.val);;
Please note that this will not handle objects that have the same key but different value.
To deal with duplicates you'd need:
var d = listA.Select(b => new { key = b.key, val = b.value })
.Union(listB.Select(b => new { key = b.key, val = b.value }))
.GroupBy(x => x.key)
.Select(x => x.First())
.ToDictionary(m => m.key, n => n.val);
Please note this keeps only the first record with a given key and records with the same key, but different value are lost.
Test code
public static void Main(string[] args)
{
List<ClassA> listA = new List<ClassA>() {
new ClassA() { key = "A", value = "1" },
new ClassA() { key = "B", value = "2" }};
List<ClassB> listB = new List<ClassB>() {
new ClassB() { key = "B", value = "2" },
new ClassB() { key = "C", value = "3" },
new ClassB() { key = "A", value = "4" }};
var d = (...)
foreach( var kvp in d ) {
Console.WriteLine($"{kvp.Key}: {kvp.Value} ");
}
}
Result
A: 1
B: 2
C: 3

LINQ Select Multiple Elements from a List<object[]>

I have a List which is populated with data from a database.
The object array has say 10 elements when populated
I want to do a LINQ Select Statement that gets returns a List<object[]> with just 2 elements. How can I select these elements 1 and 2.
I have tried the following which work for element 0 but How can I get element 0 and element 1 ??
var resultDistinct = result.Select(p => p.GetValue(0)).Distinct();
var resultDistinct2 = result.Select(p => p.ElementAt(0)).Distinct();
You could use an anonymous object for this..
var items = result.Select(p => new { ValueA = p.GetValue(0), ValueB = p.GetValue(1) });
Then access each item
foreach(var item in items)
{
var valueA = item.ValueA;
var valueB = item.ValueB;
}
You can use the Take extension method:
items.Take(x);
This will return the first x items of a collection.
If you want to skip over some elements, you can use Skip(x) before calling Take. These two methods are very often used for paging.
If you want distinct and then 2 then,
result.Select(p => p).Distinct().Take(2);
If just 2 then,
result.Take(2);
private class Foo
{
public int Item1;
public int Item2;
public int Item3;
}
static void Main(string[] args)
{
List<Foo> foos = new List<Foo>
{
new Foo() { Item1 = 1, Item2 = 2, Item3 = 3 },
new Foo() { Item1 = 4, Item2 = 5, Item3 = 6 },
new Foo() { Item1 = 7, Item2 = 8, Item3 = 9 }
};
// Create a list of lists where each list has three elements corresponding to
// the values stored in Item1, Item2, and Item3. Then use SelectMany
// to flatten the list of lists.
var items = foos.Select(f => new List<int>() { f.Item1, f.Item2, f.Item3 }).SelectMany(item => item).Distinct();
foreach (int item in items)
Console.WriteLine(item.ToString());
Console.ReadLine();
}
refer to: https://nickstips.wordpress.com/2010/09/16/linq-selecting-multiple-properties-from-a-list-of-objects/

Remove duplicate rows from a list based on selected columns?

I have a list of a class and there are two columns in this class. Now i want to remove the duplicate rows from that class using specific columns. Like remove duplicate from first column only ,remove from send column only or remove from both.So for this i am using following code. Is there any best way to do this process because in future i will have 20-25 columns in this class and at that time i have to add 20-25 if statements in this function?
public List<ContactTemp> RemoveDupliacacse(List<ContactTemp> ContactTempList, List<string> objcolumn)
{
List<ContactTemp> ContactTempListRemobdup = new List<ContactTemp>();
if (objcolumn.Contains("CITY"))
{
ContactTempListRemobdup = ContactTempList.GroupBy(s => s.City).Select(group => group.First()).ToList();
}
if (objcolumn.Contains("STATE"))
{
ContactTempListRemobdup = ContactTempList.GroupBy(s => s.State).Select(group => group.First()).ToList();
}
return ContactTempListRemobdup;
}
I think your class like
public class ContactTemp
{
public string CITY{}
public int STATE{}
}
This list "ContactTempList" will have duplicates. you want to find and remove items from this list where CITY and STATE are duplicates.
I meant that,This will return one item for each "type" (like a Distinct) (so if you have A, A, B, C it will return A, B, C)
List<ContactTemp> noDups = ContactTempList.GroupBy(d => new {d.CITY,d.STATE} )
.Select(d => d.First())
.ToList();
If you want only the elements that don't have a duplicate (so if you have A, A, B, C it will return B, C):
List<ContactTemp> noDups = ContactTempList.GroupBy(d => new {d.CITY,d.STATE} )
.Where(d => d.Count() == 1)
.Select(d => d.First())
.ToList();
You can achieve it via reflection with same signature, i.e. arbitrary number of columns:
public List<ContactTemp> RemoveDupliacacse(List<ContactTemp> ContactTempList,
List<string> objcolumn)
{
var type = typeof(ContactTemp);
foreach (var column in objcolumn)
{
var property = type.GetProperty(column);
ContactTempList = ContactTempList.GroupBy(x => property.GetValue(x))
.Select(x => x.First()).ToList();
}
return ContactTempList;
}
How about something like this?
public static List<ContactTemp> RemoveDupliacacse(
List<ContactTemp> ContactTempList,
IEnumerable<Func<ContactTemp, object>> columnSelectors)
{
IEnumerable<ContactTemp> ContactTempListRemobdup = ContactTempList;
foreach(var selector in columnSelectors)
{
ContactTempListRemobdup = ContactTempListRemobdup
.GroupBy(s => selector(s))
.Select(group => group.First());
}
return ContactTempListRemobdup.ToList();
}
You can use it like;
RemoveDupliacacse(list, new List<Func<ContactTemp, object>> {
(ContactTemp contact) => contact.State, (ContactTemp contact) => contact.City })
As you may already know, when you select multiple columns, the method removes duplicates for each column. Please check the following examples:
var list = new List<ContactTemp> {
new ContactTemp { City = "1", State = "1" },
new ContactTemp { City = "1", State = "2" },
new ContactTemp { City = "2", State = "1" },
new ContactTemp { City = "2", State = "2" }
};
foreach (var contact in RemoveDupliacacse(
list,
new List<Func<ContactTemp, object>> {
(ContactTemp contact) => contact.State,
(ContactTemp contact) => contact.City }))
{
Console.WriteLine($"City:{contact.City}, State:{contact.State}");
}
// This will output:
// City: 1, State: 1
// If you want to check duplication of the combination of the selected columns,
// you can do it like this;
foreach (var contact in RemoveDupliacacse(
list,
new List<Func<ContactTemp, object>> {
(ContactTemp contact) => new { contact.State, contact.City } }))
{
Console.WriteLine($"City:{contact.City}, State:{contact.State}");
}
// This will output:
// City: 1, State: 1
// City: 1, State: 2
// City: 2, State: 1
// City: 2, State: 2

Merge equal items with multiple lists but different length

I have 2 lists.
They are different in length but same type.
I want that an Item from List2 replaces an equal item in List1.
var item1 = new Item { Id = 1, Name = "Test1" };
var item2 = new Item { Id = 2, Name = "Test2" };
var item3 = new Item { Id = 3, Name = "Test3" };
var item4 = new Item { Id = 4, Name = "Test4" };
var item5 = new Item { Id = 5, Name = "Test5" };
var list1 = new List<Item> { item1, item2, item3, item4, item5 };
var list2 = new List<Item> { new Item { Id = 1, Name = "NewValue" } };
As a result I expect a list with 5 items where the item with Id = 1 has a value "NewValue".
How can I do that preferable with linq.
UPDATE
I extend my question:
How can the replacement of the replaced Item happen without copying all properties manually. Just imagine I have 100 properties...
This is one way to do it:
First define an equality comparer that depends only on the Id property of the Item class like this:
public class IdBasedItemEqualityComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
return x.Id == y.Id;
}
public int GetHashCode(Item obj)
{
return obj.Id.GetHashCode();
}
}
Then you can take items list1 that don't have corresponding items in list2 using the Except method and then you can concatenate that with list2 using the Concat method like this:
var result = list1.Except(list2, new IdBasedItemEqualityComparer()).Concat(list2).ToList();
Notice how I use the IdBasedItemEqualityComparer with the Except method, so that comparison is based only on Id.
Off the top of my head this is one solution
var list3 = new List<Item>();
foreach (var item in list1)
list3.Add(list2.FirstOrDefault(s => s.Id == item.Id) ?? item);
I think LEFT OUTER JOIN in Linq will be able to merge 2 lists regardless of number of properties(columns) like this:
List<Item> newItems =
(from l1 in list1
join l2 in list2 on l1.Id equals l2.Id into l12
from l2 in l12.DefaultIfEmpty()
select new { Item = (l2 == null) ? l1 : l2 }).Select(r => r.Item).ToList();

Categories