Select data table row based on column with comma separated values - c#

I have a data table that contains one column with comma separated values. I am trying to formulate a filter to use with dt.Select and need some help.
Data table looks something like this:
col1 | col2 | Some_IDs | col4
-----|------|----------|------
a | b | 1,2,3 | g
c | d | 2 | h
e | f | 1, 3 | i
If I am looking for "2", I need rows 1 and 2 returned; if I am looking for 1,3 I need rows 1 and 3 returned.
Thanks.

If you can use Linq then the following should work:
// string filter = can be: "2" , "1,3", etc
var filterArray = filter.Split(',').Select(s => s.Trim());
DataRow[] dataRows = dt.AsEnumerable()
.Where(row=>filterArray.All(f=>row.Field<string>("Some_IDs")
.Split(',')
.Any(v=>v.Trim() == f)))
.ToArray();
The Above will match the rows that contain all filter values.
If you want to rows that matches some of the filter values then use filterArray.Any instead of .All
.Where(row=>filterArray.Any(f=>row.Field<string>("Some_IDs")
.Split(',')
.Any(v=>v.Trim() == f)))

Related

Insert values in col1 of list 1 from col 2 of list 2 based on a common column

List 1:
| User Id | Latest |
+---------------------+------------------+
| 1 | 1 |
| 2 | 3 |
| 3 | 3 |
| 4 | 0 |
List 2:
| User Id | Latest | Rating |
+---------------------+------------------+------------------+
| 1 | null | 10 |
| 2 | null | 12 |
| 3 | null | 11 |
| 4 | null | 16 |
I want to insert the values of the Latest column of List1 into the Latest column of List2 based on joining/comparing values of the User Id column in both lists.
I can use a foreach loop but that would run n*m I guess and look ugly. Is there a way to do it with LINQ or efficiently?
Regards.
Junaid
var result = from i1 in List1
join i2 in List2
on i1.UserId equals i2.UserId
select new
{
i2.UserId,
i1.Latest,
i2.Rating
};
you can do it with LINQ :
Try this code :
List2.ForEach(item1 =>
{
item1.Latest = List1.FirstOrDefault(item2 => item2.UserId == item1.UserId)?.Latest;
});
Note That, Latest must be Nullable.
LINQ will never change any of the source sequences, it can only extract data from it.
You will have to enumerate over the extracted data to update your original tables.
var recordsToUpdate = List2.Join(List1, // join List2 and List1
list2Row => list2Row.UserId, // from every row in List2 take UserId
list1Row => list1Row.UserId, // from every row in List1 take UserId
(list2Row, list1Row) => new // when they match make one new object
{
Id = list2Row.UserId, // take UserId from list2
Latest = list1Row.Latest, // take Latest from list1
Rating = list2Row.Rating, // take Rating from list2
})
.ToList(); // execute the query
I don't know how you update your records. Entity framework? SQL? it will be something like this:
foreach (var recordToUpdate in recordsToUpdate)
{
UpdateRecord(recordToUpdate.UserId, recordToUpdate.Latest, recordToUpdate.Rating)
// TODO: implement this function
}
Try something like this. this may fix your issue with adding the Latest value from List1 to List2.
List2.AddRange(List1.Select(user => new List1{
Latest = user.Latest,
UserID = user.UserID
}));

How to join one row to every row in source table using LINQ

I have the following table (table A):
ID | Data |
1 | Data1 |
2 | Data2 |
3 | Data3 |
4 | Data4 |
I have table B that has 1 row:
DummyID | Dummy |
1 | Dummy1 |
I have to join table A with table B in the following way:
ID | Data |DummyID | Dummy |
1 | Data1 |1 | Dummy1 |
2 | Data2 |1 | Dummy1 |
3 | Data3 |1 | Dummy1 |
4 | Data4 |1 | Dummy1 |
Obviously I can't use any ID in the on clause.
from item in context.TableA
join dummy in context.TableB on ? = ?
select new
{
RowA=item,
Dummy=dummy
}
How could I do that with LINQ?
That's a cross join which you can get via Linq in the following way
from item in context.TableA
from dummy in context.TableB
select new
{
RowA=item,
Dummy=dummy
}
Or the following in method syntax
context.TableA.SelectMany(
item => context.TableB.Select(dummy => new { RowA = item, Dummy = dummy }));
Note that if TableB every has more than one row you'll end up with N times M rows where N is the number of rows in TableA and M is the number of rows in TableB.
No need to join at all.
from item in context.TableA
select new
{
RowA = item,
Dummy = context.TableB.FirstOrDefault()
}
Having said that, I'd have to question why you're doing this. The idea of LINQ is to get your relational data into an object-oriented form. Why not just retrieve the TableB information once and do whatever processing you need to do in-memory? It would reduce the size of the payload you're transferring from the database back to the application.
Why do you want to use join. You want to use each item in your old sequence to create a different item in your new sequence. In LINQ you would use Enumerable.Select for this:
var dummy = context.Dummy.FirstOrDefault();
var newSequence = context.TableA
.Select(itemInTableA =>
new
{
RowA = itemInTableA,
Dummy = dummy,
});

How to Join with a column having Comma Separated Values in Linq

I have one master table Category.
ID | Name
----------
1 | Category1
2 | Category2
3 | Category3
4 | Category4
And Another Table Details have field like
ID | CategoryId | Detail
--------------------
1 | 1,2,3 | Test1
2 | 3,4 | Test2
Here the Category Id stored as comma separated values.
Now i want the result as
ID | CategoryName
----------------
1 | Category1,Category2,Category3
2 | Category3,Category4
AnyOne Have idea ..??
You can use link this:
private static void commaSeperate(List<classname> obj)
{
string delimeter = ",";
Console.WriteLine(obj.Aggregate((i, j) => new classname { Name = (i.Name + delimeter + j.Name) }).Name);
Console.ReadKey();
}
This is just a sample, please modify according to your conditions.
Hope this will help you.
This could be the solution if our retrieve the data into memory first.
var q = from d in Details
from m in Master
select new {Id = d.Id, CategoryName = String.Join(m.Where(i=> d.CategoryId.Split(',').Cast<int32>().Contains(i.Id).Select(i => i.Name).ToArray(), ',')}
You can't join these two tables but I think below query would work for your result:
(from de in datacontextobj.Details
from ca in datacontextobj.Category
where de.CategoryId.Contains(ca.ID)
select de.ID, ca.Name).ToList();

How do I get a value adjacent to a Max(value) using Linq?

I have a table:
Group | BasalArea | SpeciesName
1 | 3.6 | Palustris
1 | 45.0 | MSO
2 | 4.2 | Oak
2 | 2.0 | MSO
...
From this table, I would like to get the species name with the highest basal area grouped by the Group field, which would look like this:
Group | BasalArea | SpeciesName
1 | 45.0 | MSO
2 | 4.2 | Oak
Using SQL, I can get the highest basal area:
SELECT Group, Max(BasalArea)
FROM TABLE
GROUP BY Group
I can't figure out how to also get the species name without doing some looping. Is this possible? What are the strategies for handling ties?
This is simpler in LINQ2SQL than in SQL:
var res = source.MyTable
.GroupBy(item => item.Group)
.Select(g => g.OrderByDescending(item => item.BasalArea).First())
.ToList();
This will return the list of items with largest values of BasalArea in its Group, together with SpeciesName.
In SQL you would need to join back to the original table, like this:
SELECT * FROM TABLE b
JOIN (
SELECT Group, Max(BasalArea) as BasalArea
FROM TABLE
GROUP BY Group
) t on t.Group = b.Group AND t.BasalArea = b.BasalArea
Try this:
var froup = categories.GroupBy(g => new {g.CategoryType})
.Select(g => g.OrderByDescending(i => i.CategoryID).First())
.ToArray();
What sasblinkenlight said would be the LINQ. Out of curiosity, here is a potential SQL solution.
SELECT grouped.Group, raw.SpeciesName, grouped.MaBasalArea
FROM (
SELECT Group, MAX(BasalArea) as MaxBasalArea
FROM TABLE
GROUP BY Group
) grouped
INNER JOIN TABLE raw ON grouped.MaxBasalArea = raw.BasalArea AND grouped.Group = raw.Group

LinQ with Count and Where condition

Hihi, I have a table with the following data:
SampleID | SampleKey | SampleData
1 | 1 | abc
1 | 2 | def
2 | 1 | xxx
2 | 3 | yyy
3 | 3 | zzz
3 | 4 | qqq
I would like to retrieve all rows with at least one SampleKey as 3, which should give me
2 | 1 | xxx
2 | 3 | yyy
3 | 3 | zzz
3 | 4 | qqq
both SampleID with 2 and 3 should be returned as they are considered as one pair.
Pls advice how can I achieve this? May thanks!
I would suggest not using Contains, but the built-in Join method for performance reasons..
var keys = source.Where(s => s.SampleKey == 3).Select(s => s.SampleID).Distinct();
var result = source.Join(keys, s => s.SampleID, k => k, (s, k) => s);
var idsToSelect = from x in MyTable where x.SampleKey == 3 select x.SampleID;
var results = from x in MyTable where idsToSelect.Contains(x.SampleID) select x;
Not sure I fully understand the question, but here's my bid:
var results = from r in MyTable
where r.SampleID == 3 || r.SampleKey == 3
select r;
var nResults = results.Count();
Though, I'll be honest, I don't know why your column named ID isn't actually an ID. Never mind, I think I get it now. You're linking the two columns as a unique key (or so I hope).
--
EDIT
Nappy actually had a great solution, and I'm not sure why s/he deleted it. Grabbing all rows with a 3 then rejoining them works perfect.
In the non-SQL-like syntax you could use
var groupsById = MyData.GroupBy(x => x.SampleId);
var groupsThatMatch = groupsById.Where(g => g.Any(x => x.SampleKey == 3));
var allRows = groupsThatMatch.SelectMany(g => g);
i.e group by ID, find the groups that match then flatten those back into rows. I don't know the SQL-like syntax, sorry.
DataTable dt = new System.Data.DataTable();
dt.Columns.Add("SampleID", typeof(Int32));
dt.Columns.Add("SampleKey", typeof(Int32));
dt.Columns.Add("SampleData", typeof(string));
dt.Rows.Add(1, 1, "abc");
dt.Rows.Add(1, 2, "def");
dt.Rows.Add(2, 1, "xxx");
dt.Rows.Add(2, 3, "yyy");
dt.Rows.Add(3, 3, "zzz");
dt.Rows.Add(3, 4, "qqq");
var result = from DataRow myRow in dt.Rows
where (int)myRow["SampleID"] == 3 || (int)myRow["SampleKey"] == 3
select myRow;
You could probably do with:
var result = data.Where(
y => data.Where(x => x.SampleKey == 3)
.Select(x => x.SampleID)
.Contains(y.SampleID));

Categories