How to select the data from datatable which not in an IEnumerable - c#

I have a DataTable dbcrs and I want to get only the data which is not in the following enumerable:
IEnumerable<Crs> res
Note : the key in both is id.

Here is my suggestion:
var result = dbcrs.Where(item => res.FirstOrDefault(resItem => resItem.Id == item.Id) == null);

First you need to use AsEnumerable() in order to query against the DataTable's Rows collection, then use !Contains as not in like this:
var query = from r in dbcrs.AsEnumerable()
where !( from s in res select r.Id)
.Contains(r.Id)
select r;

An example of doing this with Except and IEquatable<>
A benefit of this way is that you can define what you mean by "Equals", so that two lists which may have the same ID's but are NOT equal can still be used.
e.g. You get data from two tables, so the Id's can repeat but some other properties define if they are actually equal.
class Crs:IEquatable<Crs>
{
public int Id { get; set; }
public string Description { get; set; }
public bool Equals(Crs other)
{
if (Object.ReferenceEquals(other, null))
return false;
if (Object.ReferenceEquals(this, other))
return true;
return Id.Equals(other.Id) && Description.Equals(other.Description);
}
public override int GetHashCode()
{
int hashId = Id.GetHashCode();
int hashDescription = Description == null ? 0 : Description.GetHashCode();
return hashId ^ hashDescription;
}
}
internal static void RunMe()
{
var dataTable = new List<Crs>(){
new Crs{Id=1, Description="First"},
new Crs{Id=2, Description="Second"},
new Crs{Id=5, Description="Fifth"}
};
var enumerable = new List<Crs>(){
new Crs{Id=2, Description="Second"},
new Crs{Id=4, Description="Fourth"}
};
var distinct = dataTable.Except(enumerable);
distinct.ToList().ForEach(d => Console.WriteLine("{0}: {1}", d.Id, d.Description));
}

DataTable dt = new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
new DataColumn("Id", typeof(System.Int32)),
new DataColumn("Name", typeof(System.String))
});
dt.Rows.Add (new Object[]{1,"Test"});
dt.Rows.Add(new Object[] {2, "Test" });
var l = new Int32[] { 2, 4 };
var l1 = dt.AsEnumerable().Where(p1 => Array.IndexOf(l, p1.Field<Int32>(0))<0).CopyToDataTable();
This would return us one row because in Datatable and array both have one value in common that's 2 only. so out put will be
2, Test

Related

How to perform a recursive function in c# using linq

I have the following table:
Column_1 Column_2
val_1 | val_14
val_2 | val_17
val_1 | val_2
val_4 | null
val_1 | val_3
val_20 | val_4
val_17 | null
val_2 | val_20
val_14 | val_6
val_14 | null
Val_6 | null
val_3 | val_30
val_3 | val_19
I want to display Column_2 values
Eg: Select with Column_1 = val_1 will return (val_14, val_2, val_3) from Column_2.
Now, I want for each values in (val_14, val_2, val_3) to return also values from Column_2.
In summary:
val_1 => (val_14, val_2, val_3)
val_14 => (val_6, null)
val_6 => null
val_2 => (val_17, val_20)
val_17 => null
val_20 => (val_4)
val_4 => null
val_3 => (val_30, val_19)
etc...
Final output (val_14, val_2, val_3, val_6, val_17, val_20, val_4, val_30, val_19)
I have a function, with string parameter and list of all rows data
public List<string> MyFunction(string value)
{
return (from s in myListOfData where value.Contains(s.Column_1) select s).ToList();
}
This function return only the first level.
how can i do this query to display all children in linq? My attempts are unsuccessful.
Thank you
Desired order of records is a bit-tricky to get - looks like at first you want plain 1-st level and then traverse tree in down-left direction. It's a bit tricky.
If order is not important you can:
public List<string> MyFunction(string value)
{
return myListOfData
.Where(x => value.Contains(x.Column_1) && x.Column2 != null)
.Select(x => x.Column2)
.Aggregate(new List<string>(), (t, x) => {
t.Add(x);
t.AddRange(MyFunction(x));
return t; })
.ToList();
}
However, this results in lots of intermediate List creation. So better have enumerable:
public IEnumerable<string> MyFunction(string value)
{
foreach (var record in myListOfData.Where(x => value.Contains(x.Column_1) && x.Column2 != null)
{
yield return record.Column_2;
foreach (var child in MyFunction(record.Column_2))
yield return child;
}
}
And then take ToList() of this IEnumerable.
Still, if order is important you need two functions:
public List<string> MyFunction(string value)
{
.Where(x => value.Contains(x.Column_1) && x.Column2 != null)
.Select(x => new Tuple<string, IEnumerable<string>>(x.Column2, Traverse(x.Column2))
.Aggregate(new List<string>(), (t, x) => {
t.Add(x.Item1);
t.AddRange(x.Item2);
return t; })
.ToList();
}
public IEnumerable<string> Traverse(string value)
{
foreach (var record in myListOfData.Where(x => value.Contains(x.Column_1) && x.Column2 != null)
{
yield return record.Column_2;
foreach (var child in MyFunction(record.Column_2))
yield return child;
}
}
Assuming this is all in memory and nothing to do with an ORM.
You could use recursion. However, queues and stacks are safer and easier to debug.
Given some weird ill-defined class
public class Data
{
public int? Col1 { get; set; }
public int? Col2 { get; set; }
public Data(int? col1, int? col2)
{
Col1 = col1;
Col2 = col2;
}
}
You could use an iterator method and a Queue
public static IEnumerable<int> GetRecusive(List<Data> source,int val)
{
var q = new Queue<int>();
q.Enqueue(val);
while (q.Any())
{
var current = q.Dequeue();
var potential = source.Where(x => x.Col1 == current && x.Col2 != null);
foreach (var item in potential)
{
yield return item.Col2.Value;
q.Enqueue(item.Col2.Value);
}
}
}
Usage
// some ill-defined test data
var list = new List<Data>()
{
new Data(1, 14),
new Data(2, 17),
new Data(1, 2),
new Data(4, null),
new Data(1, 3),
new Data(20, 4),
new Data(17, null),
new Data(2, 20),
new Data(14, 6),
new Data(14, null),
new Data(6, null),
new Data(3, 30),
new Data(3, 19),
};
var results = GetRecusive(list,1);
// compose as a comma separated list
Console.WriteLine(string.Join(", ",results));
Output
14, 2, 3, 6, 17, 20, 30, 19, 4
Full Demo Here
If you like, you can turn it into an extension method to give you a LINQ Chain Method feel
public static IEnumerable<int> GetRecusive(this List<Data> source, int val)
Important Note : If you have a circular references then kiss your app goodbye. This will be the same for recursion or queues. If you need to protect against this, then I suggest using a HashSet of visited ids

Linq group by with return count

Net core and Linq. I have table like below
Orders Table
OrderId Status
1 New
2 New
3 In Progress
4 In Progress
5 Closed
6 Closed
I have below model
public class SummaryEntity
{
public int New { get; set; }
public int InProgress { get; set; }
public int Closed { get; set; }
}
Then I need to return and bind to below model like below
New : 2
InProgress : 2
Closed : 2
I have tried something like below
SummaryEntity result = (from item in Orders
group item by new { item.Status } into g
select new SummaryEntity{ //not sure how to get count and assign it to model }
);
I am finding hard to group by and assign values to model. Can someone help me to write query. Any help would be appreciated. Thank you
You've already grouped the items by status, so g.Key will contain the status value and g itself is an enumerable of the grouped items. If you want to calculate their count use Count(), eg :
var counts = from item in Orders
group item by new { item.Status } into g
select new {status=g.Key, count=g.Count()};
This will return one object per status value with its count. Getting different columns for each status is essentially pivoting, converting the rows to columns.
In this case though, where you know the status names in advance, you can convert the results into a dictionary and retrieve the counts by name, eg :
var dict=counts.ToDictionary(x=>x.status,x=>x.count);
var model= new SummaryEntity
{
New = dict.TryGetValue("New",out var c_n)
? c_n : 0,
InProgress = dict.TryGetValue("InProgress",out var c_p)
? c_p : 0,
Closed = dict.TryGetValue("Closed",out var c_c)
? c_c : 0,
};
Dictionary.TryGetValue is used to avoid exceptions if a status value is missing
You model makes no sense. You have only status not New, InProgress, Closed. Try following :
DataTable dt = new DataTable();
dt.Columns.Add("OrderId", typeof(int));
dt.Columns.Add("Status", typeof(string));
dt.Rows.Add( new object[] { 1, "New"});
dt.Rows.Add( new object[] { 2, "New"});
dt.Rows.Add( new object[] { 3, "In Progress"});
dt.Rows.Add( new object[] { 4, "In Progress"});
dt.Rows.Add( new object[] { 5, "Closed"});
dt.Rows.Add( new object[] { 6, "Closed"});
var results = dt.AsEnumerable()
.GroupBy(x => x.Field<string>("Status"))
.Select(x => new { status = x.Key, count = x.Count() })
.ToList();

How to use dictionary in c# to compare two lists

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));

C# Linq rows to column

I would like to turn linq result into columns from rows, the field names are user changeable so I need the function to be dynamic.
sample data
ID: 331 FieldName: "BusinessCategory" FieldContents: "Regulatory"
ID: 331 FieldName: "PriorityGroup" FieldContents: "Must Do"
ID: 332 FieldName: "BusinessCategory" FieldContents: "Financial"
ID: 332 FieldName: "PriorityGroup" FieldContents: "Should Do"
Turn it into (sample end output)
ID BusinessCategory PriorityGroup
331 Regulatory Must Do
332 Financial Should DO
Here is the code block to extract to fieldnames and contents from the database.
public static IEnumerable<InitProjectValues1> GetProgramInitiativeAttributesPart1(int id)
{
using (dpm db = new dpm())
{
string partit = (string)HttpContext.Current.Session["sitePart"];
var configrefs = from c in (
from e in db.Metrics
join j in db.ProgramLink on e.ProjectRef equals j.LinkedProject
where (j.ProjectRef == id) && e.PartitNo == partit
select new
{
FieldName = e.FieldName,
FieldContents = e.MetricValue,
ProjectRef = e.ProjectRef,
})
select new InitProjectValues1
{
ProjectRef = c.ProjectRef,
FieldName = c.FieldName,
FieldContents = c.FieldContents,
}; //somewhere here would be the code to cover this into a single row per ProjectRef number.
return configrefs.ToList();
}
}
Here is the data model.
public class InitProjectValues1
{
public int? ProjectRef { get; set; }
public string FieldName { get; set; }
public string FieldContents { get; set; }
}
I really don't know where to go from here, hoping someone can provide guidance / sample code
The kind of operation you need is called a pivot. You are effectively rotating the table around a unique productRef and changing the rows to columns.
You could try this which makes use of a dynamic object which you require for dynamic column generation.
var configrefs = from c in (
from e in db.Metrics
join j in db.ProgramLink on e.ProjectRef equals j.LinkedProject
where (j.ProjectRef == id) && e.PartitNo == partit
select new
{
FieldName = e.FieldName,
FieldContents = e.MetricValue,
ProjectRef = e.ProjectRef,
}).ToArray();
return configrefs.ToPivotArray(
i => i.FieldName,
i => i.ProjectRef,
items => items.Any() ? items.FirstOrDefault().FieldContents : null);
Private method to get dynamic object:
private static dynamic GetAnonymousObject(IEnumerable<string> columns, IEnumerable<object> values)
{
IDictionary<string, object> eo = new ExpandoObject() as IDictionary<string, object>;
int i;
for (i = 0; i < columns.Count(); i++)
{
eo.Add(columns.ElementAt<string>(i), values.ElementAt<object>(i));
}
return eo;
}
And the extension method
public static dynamic[] ToPivotArray<T, TColumn, TRow, TData>(
this IEnumerable<T> source,
Func<T, TColumn> columnSelector,
Expression<Func<T, TRow>> rowSelector,
Func<IEnumerable<T>, TData> dataSelector)
{
var arr = new List<object>();
var cols = new List<string>();
String rowName = ((MemberExpression)rowSelector.Body).Member.Name;
var columns = source.Select(columnSelector).Distinct();
cols =(new []{ rowName}).Concat(columns.Select(x=>x.ToString())).ToList();
var rows = source.GroupBy(rowSelector.Compile())
.Select(rowGroup => new
{
Key = rowGroup.Key,
Values = columns.GroupJoin(
rowGroup,
c => c,
r => columnSelector(r),
(c, columnGroup) => dataSelector(columnGroup))
}).ToArray();
foreach (var row in rows)
{
var items = row.Values.Cast<object>().ToList();
items.Insert(0, row.Key);
var obj = GetAnonymousObject(cols, items);
arr.Add(obj);
}
return arr.ToArray();
}
Modified the ToPivotArray extension to handle multiple column selectors (using an anonymous class as the column selector)
public static dynamic[] ToPivotArrayNew<T, TColumn, TRow, TData>(
this IEnumerable<T> source,
Func<T, TColumn> columnSelector,
Expression<Func<T, TRow>> rowSelector,
Func<IEnumerable<T>, TData> dataSelector)
{
var arr = new List<object>();
var cols = new List<string>();
List<string> rowNames = new List<string>();
bool isObjectSelector = false;
if (rowSelector.Body.GetType() == typeof(MemberExpression))
{
rowNames.Add(((MemberExpression)rowSelector.Body).Member.Name);
}
else if (rowSelector.Body.GetType() == typeof(NewExpression))
{
isObjectSelector = true;
((NewExpression)rowSelector.Body).Members.ToList().ForEach(m => rowNames.Add(m.Name));
}
var columns = source.Select(columnSelector).Distinct();
cols = rowNames.ToArray().Concat(columns.Select(x => x.ToString())).ToList();
var rows = source.GroupBy(rowSelector.Compile())
.Select(rowGroup => new
{
Key = rowGroup.Key,
Values = columns.GroupJoin(
rowGroup,
c => c,
r => columnSelector(r),
(c, columnGroup) => dataSelector(columnGroup))
}).ToArray();
foreach (var row in rows)
{
var items = row.Values.Cast<object>().ToList();
if (isObjectSelector)
{
for (int i = 0; i < rowNames.Count(); i++)
{
items.Insert(i, row.Key.GetType().GetProperty(rowNames[i]).GetValue(row.Key));
}
}
else
{
items.Insert(0, row.Key);
}
var obj = GetAnonymousObject(cols, items);
arr.Add(obj);
}
return arr.ToArray();
}

Linq: find similar objects from two different lists

I've got two separate lists of custom objects. In these two separate lists, there may be some objects that are identical between the two lists, with the exception of one field ("id"). I'd like to know a smart way to query these two lists to find this overlap. I've attached some code to help clarify. Any suggestions would be appreciated.
namespace ConsoleApplication1
{
class userObj
{
public int id;
public DateTime BirthDate;
public string FirstName;
public string LastName;
}
class Program
{
static void Main(string[] args)
{
List<userObj> list1 = new List<userObj>();
list1.Add(new userObj()
{
BirthDate=DateTime.Parse("1/1/2000"),
FirstName="John",
LastName="Smith",
id=0
});
list1.Add(new userObj()
{
BirthDate = DateTime.Parse("2/2/2000"),
FirstName = "Jane",
LastName = "Doe",
id = 1
});
list1.Add(new userObj()
{
BirthDate = DateTime.Parse("3/3/2000"),
FirstName = "Sam",
LastName = "Smith",
id = 2
});
List<userObj> list2 = new List<userObj>();
list2.Add(new userObj()
{
BirthDate = DateTime.Parse("1/1/2000"),
FirstName = "John",
LastName = "Smith",
id = 3
});
list2.Add(new userObj()
{
BirthDate = DateTime.Parse("2/2/2000"),
FirstName = "Jane",
LastName = "Doe",
id = 4
});
List<int> similarObjectsFromTwoLists = null;
//Would like this equal to the overlap. It could be the IDs on either side that have a "buddy" on the other side: (3,4) or (0,1) in the above case.
}
}
}
I don't know why you want a List<int>, i assume this is what you want:
var intersectingUser = from l1 in list1
join l2 in list2
on new { l1.FirstName, l1.LastName, l1.BirthDate }
equals new { l2.FirstName, l2.LastName, l2.BirthDate }
select new { ID1 = l1.id, ID2 = l2.id };
foreach (var bothIDs in intersectingUser)
{
Console.WriteLine("ID in List1: {0} ID in List2: {1}",
bothIDs.ID1, bothIDs.ID2);
}
Output:
ID in List1: 0 ID in List2: 3
ID in List1: 1 ID in List2: 4
You can implement your own IEqualityComparer<T> for your userObj class and use that to run a comparison between the two lists. This will be the most performant approach.
public class NameAndBirthdayComparer : IEqualityComparer<userObj>
{
public bool Equals(userObj x, userObj y)
{
return x.FirstName == y.FirstName && x.LastName == y.LastName && x.BirthDate == y.BirthDate;
}
public int GetHashCode(userObj obj)
{
unchecked
{
var hash = (int)2166136261;
hash = hash * 16777619 ^ obj.FirstName.GetHashCode();
hash = hash * 16777619 ^ obj.LastName.GetHashCode();
hash = hash * 16777619 ^ obj.BirthDate.GetHashCode();
return hash;
}
}
}
You can use this comparer like this:
list1.Intersect(list2, new NameAndBirthdayComparer()).Select(obj => obj.id).ToList();
You could simply join the lists on those 3 properties:
var result = from l1 in list1
join l2 in list2
on new {l1.BirthDate, l1.FirstName, l1.LastName}
equals new {l2.BirthDate, l2.FirstName, l2.LastName}
select new
{
fname = l1.FirstName,
name = l1.LastName,
bday = l1.BirthDate
};
Instead of doing a simple join on just one property (column), two anonymous objects are created new { prop1, prop2, ..., propN}, on which the join is executed.
In your case we are taking all properties, except the Id, which you want to be ignored and voila:
Output:
And Tim beat me to it by a minute
var similarObjectsFromTwoLists = list1.Where(x =>
list2.Exists(y => y.BirthDate == x.BirthDate && y.FirstName == x.FirstName && y.LastName == x.LastName)
).ToList();
This is shorter, but for large list is more efficient "Intersect" or "Join":
var similarObjectsFromTwoLists =
list1.Join(list2, x => x.GetHashCode(), y => y.GetHashCode(), (x, y) => x).ToList();
(suposing GetHashCode() is defined for userObj)
var query = list1.Join (list2,
obj => new {FirstName=obj.FirstName,LastName=obj.LastName, BirthDate=obj.BirthDate},
innObj => new {FirstName=innObj.FirstName, LastName=innObj.LastName, BirthDate=innObj.BirthDate},
(obj, userObj) => (new {List1Id = obj.id, List2Id = userObj.id}));
foreach (var item in query)
{
Console.WriteLine(item.List1Id + " " + item.List2Id);
}

Categories