First time using lambda function on c# - c#

Hello everyone this is my first time asking a question on stack overflow, so I'll be as descriptive as possible. I've been trying to wrap my head around the lambda function and I think that this would be an appropriate time to use the function.
I have an sql table with a column machineNo and another column named count as a flag of yes or no whether its counted or not.
My objective is to group columns based on the machineNo column in sql. I've returned the list from sql with no problems.
I have
Spot 2
Spot 1
Spot 1
Spot 1
Spot 1
Its supposed to show e.g
lbl1.Text = 4
lbl2.Text = 1
public void listlambda()
{
con.OpenConnection();
List<string> mchno = con.LoadList("Select mchNo from tbl_mch_ability where count = 0", "mchNo");
var num = mchno.GroupBy(n => n == "mchNo");
lbl1.Text = mchno.GroupBy(m => m == "Spot 1").Count().ToString();
lbl2.Text = mchno.GroupBy(m => m == "Spot 2").Count().ToString();
}
I've tried a lot of examples from stack overflow but my label always shows two.

You can not apply == on IGrouping. You need to change the groupBy as
var num = mchno.GroupBy(n => n);
and later the text box assignment as
lbl1.Text = num .Count(m => m == "Spot 1").ToString();
lbl2.Text = num .Count(m => m == "Spot 2").ToString();

Related

C# - LINQ - specific query select

I would like to get a specific select with the best performance possible
I currently have a code like this:
var getData = from x in dbContex.MyTable
where x.Car == "audi"
select x;
MyTable has three columns:
Car
ProductionYear
Color
I would like to download a specific vehicle brand with the year of manufacture that has a specific color.
I am currently doing it with reference to the code above:
foreach (var item in getData)
{
if (item.color == "blue") item.ProductionYear = "1999";
if (item.color == "red") item.ProductionYear = "2003";
// etc (...)
}
I would like to assign a specific year for a given car and color if I find it.
A very prosaic example above.
Problem - I currently have 100 colors and only want to check 10 colors.
How can I do it faster so as not to search the entire list and not to use IF statements?
Create an array of the colors, then use Contains method in the predicate expression:
var colors = new [] { "red", "blue" };
var results = dbContext.MyTable
.Where( x => "audi" == x.Car
&& colors.Contains( x.color ) );
Then use a switch statement to assign your years:
foreach(var car in results)
{
car.ProductionYear = car.color switch
{
"blue" => 1999,
"red" => 2003,
_ => // unexpected result, throw exception
}
}
You could also define the years with the colors then work the year assignment into the LINQ query:
var colorYears = new []
{
new {
color = "red",
ProductionYear = 2003,
},
...
};
var colors = colorYears.Select( x => x.color ).ToArray();
var results = (...query from above...)
// enumerate
.ToArray()
// join datasource results with your colorYears array
// on the `color` property
.Join( colorYears,
c => c.color,
cy => cy.color,
( c, cy ) =>
{
// assign the configured production year for the color
c.ProductionYear = cy.ProductionYear;
// return the entity
return c;
} );
There is no scenario, where you don`t use if or switch clause. You want to change data, which is not LINQ case. Only thing I would do - go to switch, rather than If clause.
I don't want to create a new thread so I will write here - a question about LINQ of course and data comparison in the array
I have this code:
var dataTest = (dbContex.MyTable.Where(x => x.col1 == str1
&& x.col2 == str2 && x.col3 == str3).Select(x => x.ID ).FirstOrDefault());
I want to check if there is such a row for these three fields and get its ID - everything is fine when I have values, but when any field is NULL - it cannot compare it and returns information that there is no such record.
the goal is to check if there is a duplicate with these values ​​in MyTable based on these three fields - except that each of these fields can be NULL
how best to compare NULL?

Retrieving data from sql to datagridview using LinqToSql

I have a datatable in sql and a datagridview in winform. datatable holds measurement results from a mould with a MouldID. For every measurement 50 lines of results are logged to table. To track measurement count for same mould, i also have MeasId column which incremented by 1 for every measurement input. Please see picture for table view.
What i need to do, retrieve only the rows with choosen MouldID (from a combobox) with last MeasID.
I tried following codes but i couldn't figure out how to group this rows with MeasId.
using (LinqDataClassesDataContext dataContext = new
LinqDataClassesDataContext())
{
// attemp 1
var query=dataContext.SupplierVals
.Where(m=>m.MouldID==comboBMouldID.SelectedValue.ToString())
.OrderByDescending(m => m.MeasId).FirstOrDefault();
// attemp 2
var query=dataContext.SupplierVals
.Where(mr=>mr.MouldID==comboBMouldID.SelectedValue.ToString())
.OrderByDescending(mr => mr.MeasId).Select();
// attemp 3
var query = (from x in dataContext.SupplierVals
where x.MouldID == comboBMouldID.SelectedValue.ToString()
select x).First();
// attemp 4
var query = from x in dataContext.SupplierVals
where x.MouldID == comboBMouldID.SelectedValue.ToString()
group x by x.MeasId into grp
select grp.OrderByDescending(x => x.MeasId).First();
daGridUnused.AutoGenerateColumns = false;
daGridUnused.Columns["unusedShowDist"].DataPropertyName = "Distnc";
daGridUnused.Columns["unusedShowAper"].DataPropertyName = "Apert";
daGridUnused.Columns["unusedShowTap"].DataPropertyName = "Taper";
daGridUnused.DataSource = query;
}
None of these queries return what i need from datatable.
What am i doing wrong?
It seems that you were almost there. You simply need to filter also by the Max value and order by the ValueId:
string mouldId = comboBMouldID.SelectedValue.ToString();
int max = dataContext.SupplierVals
.Where(m=>m.MouldID == mouldId)
.Max(m => m.MeasId);
var query=dataContext.SupplierVals
.Where(m=>m.MouldID == mouldId && m.MeasId == max).ToList();
disclaimer: this query can surely be optimized, I am working on a better solution

Sort column in DataTable with same values

I have a question about sorting in Datatable. I have a table like below and want to sort it from small to big. The problem is when i have same numbers, i want to have the first as the last and so on...
Table:-----------------------------------After Sorting:
Name Bit Size Name Bit Size (corrected)
A 0 1 A 0 1
C 1 2 C 1 2
B 1 3 B 1 3
D 1 1 D 1 1
Result that i want:
Name Bit Size (corrected)
A 0 1
D 1 1
B 1 3
C 1 2
My Code:
arraySBit.DefaultView.Sort = "Bit";
arraySBit = arraySBit.DefaultView.ToTable();
You can use Linq-To-DataTable:
var tblSorted = table.AsEnumerable()
.OrderBy(r => r.Field<int>("Bit"))
.CopyToDataTable();
Edit: But actually DataView.Sort should also work (tested).
Since you have edited your question. Your requirement seems weired. If the Bit is the same you don't want to order by something but you want to reverse the "order" of the rows of the equal rows (so the Ordinal position in the DataTable).
This does what you want although i'm not sure that it's really what you need:
DataTable tblSorted = table.AsEnumerable()
.Select((Row, Ordinal) => new {Row,Ordinal})
.OrderBy(x => x.Row.Field<int>("Bit"))
.ThenByDescending(x => x.Ordinal)
.Select(x => x.Row)
.CopyToDataTable();
Basically it passes the index of the row in the table via this overload Enumerable.Select into an anonymous type. Then it'll sort by Bit first and the index/ordinal second.
A workaround,
After these lines, you should have result in arrySBit as you want
DataTable arrySBitClone = arrySBit.Copy();
arrySBit.DefaultViewSort.Sort = "Bit";
bool different = false;
for(int i=0; i<arrySBit.Rows.Count; i++)
{
if(arrySBit.Rows[i]["Bit"]!=arrySBitClone.Rows[i]["Bit"])
{
difference = true;
break;
}
}
if(!different)
{
arrySBit = arrySBit.Copy();
}
if i'm getting right your question.
why not use Select?
DataTable dt = arraySBit.Select("", "Bit, Size").CopyToDataTable();
The first parameter of the Select Method is a condition to filter, the second is an Order By, so it should work

most matched field value

I have a DataTable. I can also use Linq.
In a DataTable have many columns, and rows. One of the column is called as feedCode. its type is string. in database it's length is 7 varchar, nullable.
feedCode may contain values as 9051245, 9051246, 9051247, 9031454, 9021447.
Method must return most matched (in this case starting with 905) value 905 (first 3 character of string)?
thanks.
Try to use this code:
var feedCodes = new string[] { "9051245", "9051246", "9051247", "9051245", "9031454", "9021447" };
var mostOccuring = feedCodes.Where(feedCode => feedCode != null)
.GroupBy(feedCode => feedCode.Length < 3 ? feedCode : feedCode.Substring(0, 3))
.OrderByDescending(group => group.Count())
.FirstOrDefault();
if(mostOccuring == null)
{
//some exception handling
}
else
{
//process mostoccuring.Key
}
this code also handle feedcodes with length less than 3 (even empty strings). If you don't want to use them just filter them out in where statement.
Maybe i didn't understand your question correctly but maybe this will be a starting point for your:
//The feedCodes (i put one in two times, to have one appearing most often)
var values = new string[] { "9051245", "9051246", "9051247", null, "", "9051245", "9031454", "9021447" };
//Just filter the list for filled up values
var query = values.Where(value => !String.IsNullOrEmpty(value))
//and group them by their starting text
.GroupBy(value => value.Substring(0, 3))
//order by the most occuring group first
.OrderByDescending(group => group.Count());
//Iterate over all groups or just take the first one with query.First() or query.FirstOrDefault()
foreach (var group in query)
{
Console.WriteLine(group.Key + " Count: " + group.Count());
}

Use LINQ to group a sequence by date with no gaps

I'm trying to select a subgroup of a list where items have contiguous dates, e.g.
ID StaffID Title ActivityDate
-- ------- ----------------- ------------
1 41 Meeting with John 03/06/2010
2 41 Meeting with John 08/06/2010
3 41 Meeting Continues 09/06/2010
4 41 Meeting Continues 10/06/2010
5 41 Meeting with Kay 14/06/2010
6 41 Meeting Continues 15/06/2010
I'm using a pivot point each time, so take the example pivot item as 3, I'd like to get the following resulting contiguous events around the pivot:
ID StaffID Title ActivityDate
-- ------- ----------------- ------------
2 41 Meeting with John 08/06/2010
3 41 Meeting Continues 09/06/2010
4 41 Meeting Continues 10/06/2010
My current implementation is a laborious "walk" into the past, then into the future, to build the list:
var activity = // item number 3: Meeting Continues (09/06/2010)
var orderedEvents = activities.OrderBy(a => a.ActivityDate).ToArray();
// Walk into the past until a gap is found
var preceedingEvents = orderedEvents.TakeWhile(a => a.ID != activity.ID);
DateTime dayBefore;
var previousEvent = activity;
while (previousEvent != null)
{
dayBefore = previousEvent.ActivityDate.AddDays(-1).Date;
previousEvent = preceedingEvents.TakeWhile(a => a.ID != previousEvent.ID).LastOrDefault();
if (previousEvent != null)
{
if (previousEvent.ActivityDate.Date == dayBefore)
relatedActivities.Insert(0, previousEvent);
else
previousEvent = null;
}
}
// Walk into the future until a gap is found
var followingEvents = orderedEvents.SkipWhile(a => a.ID != activity.ID);
DateTime dayAfter;
var nextEvent = activity;
while (nextEvent != null)
{
dayAfter = nextEvent.ActivityDate.AddDays(1).Date;
nextEvent = followingEvents.SkipWhile(a => a.ID != nextEvent.ID).Skip(1).FirstOrDefault();
if (nextEvent != null)
{
if (nextEvent.ActivityDate.Date == dayAfter)
relatedActivities.Add(nextEvent);
else
nextEvent = null;
}
}
The list relatedActivities should then contain the contiguous events, in order.
Is there a better way (maybe using LINQ) for this?
I had an idea of using .Aggregate() but couldn't think how to get the aggregate to break out when it finds a gap in the sequence.
Here's an implementation:
public static IEnumerable<IGrouping<int, T>> GroupByContiguous(
this IEnumerable<T> source,
Func<T, int> keySelector
)
{
int keyGroup = Int32.MinValue;
int currentGroupValue = Int32.MinValue;
return source
.Select(t => new {obj = t, key = keySelector(t))
.OrderBy(x => x.key)
.GroupBy(x => {
if (currentGroupValue + 1 < x.key)
{
keyGroup = x.key;
}
currentGroupValue = x.key;
return keyGroup;
}, x => x.obj);
}
You can either convert the dates to ints by means of subtraction, or imagine a DateTime version (easily).
In this case I think that a standard foreach loop is probably more readable than a LINQ query:
var relatedActivities = new List<TActivity>();
bool found = false;
foreach (var item in activities.OrderBy(a => a.ActivityDate))
{
int count = relatedActivities.Count;
if ((count > 0) && (relatedActivities[count - 1].ActivityDate.Date.AddDays(1) != item.ActivityDate.Date))
{
if (found)
break;
relatedActivities.Clear();
}
relatedActivities.Add(item);
if (item.ID == activity.ID)
found = true;
}
if (!found)
relatedActivities.Clear();
For what it's worth, here's a roughly equivalent -- and far less readable -- LINQ query:
var relatedActivities = activities
.OrderBy(x => x.ActivityDate)
.Aggregate
(
new { List = new List<TActivity>(), Found = false, ShortCircuit = false },
(a, x) =>
{
if (a.ShortCircuit)
return a;
int count = a.List.Count;
if ((count > 0) && (a.List[count - 1].ActivityDate.Date.AddDays(1) != x.ActivityDate.Date))
{
if (a.Found)
return new { a.List, a.Found, ShortCircuit = true };
a.List.Clear();
}
a.List.Add(x);
return new { a.List, Found = a.Found || (x.ID == activity.ID), a.ShortCircuit };
},
a => a.Found ? a.List : new List<TActivity>()
);
Somehow, I don't think LINQ was truly meant to be used for bidirectional-one-dimensional-depth-first-searches, but I constructed a working LINQ using Aggregate. For this example I'm going to use a List instead of an array. Also, I'm going to use Activity to refer to whatever class you are storing the data in. Replace it with whatever is appropriate for your code.
Before we even start, we need a small function to handle something. List.Add(T) returns null, but we want to be able to accumulate in a list and return the new list for this aggregate function. So all you need is a simple function like the following.
private List<T> ListWithAdd<T>(List<T> src, T obj)
{
src.Add(obj);
return src;
}
First, we get the sorted list of all activities, and then initialize the list of related activities. This initial list will contain the target activity only, to start.
List<Activity> orderedEvents = activities.OrderBy(a => a.ActivityDate).ToList();
List<Activity> relatedActivities = new List<Activity>();
relatedActivities.Add(activity);
We have to break this into two lists, the past and the future just like you currently do it.
We'll start with the past, the construction should look mostly familiar. Then we'll aggregate all of it into relatedActivities. This uses the ListWithAdd function we wrote earlier. You could condense it into one line and skip declaring previousEvents as its own variable, but I kept it separate for this example.
var previousEvents = orderedEvents.TakeWhile(a => a.ID != activity.ID).Reverse();
relatedActivities = previousEvents.Aggregate<Activity, List<Activity>>(relatedActivities, (items, prevItem) => items.OrderBy(a => a.ActivityDate).First().ActivityDate.Subtract(prevItem.ActivityDate).Days.Equals(1) ? ListWithAdd(items, prevItem) : items).ToList();
Next, we'll build the following events in a similar fashion, and likewise aggregate it.
var nextEvents = orderedEvents.SkipWhile(a => a.ID != activity.ID);
relatedActivities = nextEvents.Aggregate<Activity, List<Activity>>(relatedActivities, (items, nextItem) => nextItem.ActivityDate.Subtract(items.OrderBy(a => a.ActivityDate).Last().ActivityDate).Days.Equals(1) ? ListWithAdd(items, nextItem) : items).ToList();
You can properly sort the result afterwards, as now relatedActivities should contain all activities with no gaps. It won't immediately break when it hits the first gap, no, but I don't think you can literally break out of a LINQ. So it instead just ignores anything which it finds past a gap.
Note that this example code only operates on the actual difference in time. Your example output seems to imply that you need some other comparison factors, but this should be enough to get you started. Just add the necessary logic to the date subtraction comparison in both entries.

Categories