C# Linq Group by with Distinct not working - c#

I've reviewed numerous articles here on SO about how to group by using a LINQ statement on a list object but the following code fails to group anything even though all the variables have the exact same values. Could someone please tell me why this might not be working? Does it have something to do with LINQ deferred execution? I've tried a sample where I included a second select statement which did not help. I'm passing in a list<MyClass> object which has two identical items and one that is not, so when this statement executes I should receive back only two items.
Actual values in the code:
Type = 1, Capacity = 50, Goal = "Teach algebra",
Attachments = "", Hours = 1, TypeDesignation = 3,
TypeControl = 1, Supplies = 0
TypeControl and Supplies are both objects themselves. Supplies is empty.
TypeControl contains Id = 23, Text = "Math", Active = 1
var test = newclass.GroupBy(n => new
{
n.Type,
n.Capacity,
n.Goal,
n.Attachments,
n.Hours,
n.TypeDesignation,
n.TypeControl,
n.Supplies
}).Distinct().Select(n => new MyClass()
{
Type = n.Key.Type,
Capacity = n.Key.Capacity,
Goal = n.Key.Goal ,
Attachments = n.Key.Attachments,
Hours = n.Key.Hours ,
TypeDesignation = n.Key.TypeDesignation,
TypeControl = n.Key.TypeControl,
Supplies = n.Key.Supplies
});

The answer to my question was that the all the objects inside were, in fact, not equal, in that I had a sub-object that during the group by one object was read as being null while the other was reading as being 0 so they never could group together but when I looked at them with intellisense they always both said null. I resolved the issue by looping through my list and using a comparer method to drill down into the sub-objects and find the differences.
VerifyDuplicates duplicates = new VerifyDuplicates();
List<MyClass> newClass = new List<MyClass>();
classes = classes.OrderBy(s => s.Type).ToList();
List<int> idsToDelete = new List<int>();
for (int i = 0; i < classes.Count; i++)
{
if (i + 1 < classes.Count)
{
var x = duplicates.Compare(classes[i], classes[i + 1]);
if (x == 1)
idsToDelete.Add(classes[i+1].Id);
}
}
newClass = classes.Where(n => !idsToDelete.Contains(n.Id)).Select(n => n).ToList();
public class VerifyDuplicates : Comparer<MyClass>
{
public override int Compare(MyClass x, MyClass y)
{
int p = 0;
if(x != null && y != null)
{
if(x.Type.Equals(y.Type)) { p += 1; }
if(x.Attachments.Equals(y.Attachments)) { p += 1; }
if(x.Capacity.Equals(y.Capacity)) { p += 1; }
if(x.Goal.Equals(y.Goal)) { p += 1; }
if(x.Hours.Equals(y.Hours)) { p += 1; }
if(x.TypeDesignation.Equals(y.TypeDesignation)) { p += 1; }
if(x.TypeControl != null && y.TypeControl != null)
if(x.TypeControl[0].Equals(y.TypeControl[0])) { p += 1; }
if(x.Supplies != null && y.Supplies!= null)
if (x.Supplies.Equals(y.Supplies)) { p += 1; }
return p;
}
}
}

Related

Simplify a method with LINQ to update a series of nested property values

Can this be simplified?
public int ReplaceNameInHistoryForPublisher(string OldName, string NewName)
{
int iSize = _DutyAssignments.Count;
int iTotalReplaced = 0, iTotal = 0;
try
{
for (int i = 0; i < iSize; i++)
{
DutyAssignmentEntry oEntry = _DutyAssignments[i];
int iSizeAssign = oEntry.Assignments.Count;
for(int iAssign = 0; iAssign < iSizeAssign; iAssign++)
{
if(oEntry.Assignments[iAssign].Name == OldName)
{
oEntry.Assignments[iAssign].Name = NewName;
iTotal++;
}
}
if(iTotal > 0)
{
_DutyAssignments[i] = oEntry;
iTotalReplaced += iTotal;
}
}
return iTotalReplaced;
}
catch (Exception ex)
{
SimpleLog.Log(ex);
return 0;
}
}
I have a List of DutyAssignmentEntry objects. Each of these objects has an Assignments property. As expected, that variable is a List of Assignment objects.
The Assignment object has a Name property which is what i am looking at to update.
My code works but I wondering if it can be improved with LINQ?
Yes, you can simplify it:
public int ReplaceNameInHistoryForPublisher(string OldName, string NewName)
{
var assignmentsToUpdate = _DutyAssignments
.SelectMany(da => da.Assignments.Where(a => a.Name == OldName))
.ToList();
assignmentsToUpdate.ForEach(x => x.Name = NewName);
return assignmentsToUpdate.Count;
}
But note that LINQ is not the right tool to update a collection but to query it. You can use it to find out what you have to update. I hide the loops in the LINQ query and in List<T>.ForEach.
Btw, Assignment is a reference type, so you can simply change the Name property, you don't need to overwrite this reference in the list with itself(_DutyAssignments[i] = oEntry).

In C#, how can I order this list of objects by which item is greater?

I have a simple class called Team, that looks like this:
public class Team
{
public Team ParentTeam;
public string Name;
}
So it has a Name and a reference to another team that is its Parent Team.
I now have a list of Teams that I am getting back from a function
List<Team> list = GetTeamsList();
Given, a few assumptions:
All teams have a ParentTeam except one (the top team)
Every team returned in the list is part of the same hierarchy and its only a single hierarchy (no 2 teams at the same "level")
I now need to take the results of this function and order the list by the hierarchy
So imagine we have the following team information:
|| Team Name || Parent Team Name ||
||-----------||------------------||
|| Team A || Team B ||
|| Team B || Team C ||
|| Team C || Team D ||
|| Team D || null ||
but the GetTeamsList() function returns the teams in any random order. For example, it might come back list this:
var teamA = GetTeamA();
var teamB = GetTeamB();
var teamC = GetTeamC();
var teamD = GetTeamD();
List<Team> list = new List() { teamD, teamA, teamB, teamC };
where I need to reorder this list so it looks like this:
List<Team> list = new List() { teamA, teamB, teamC, teamD };
How could I reorder a list into the "correct" order based on the team hierarchy?
Several of the solutions given so far are correct, and all of them are at least quadratic in the number of teams; they will be inefficient as the number of teams grows large.
Here's a solution which is (1) linear, (2) shorter, and (3) easier to understand than some of the other solutions so far:
static IEnumerable<Team> SortTeams(IEnumerable<Team> teams)
{
var lookup = teams.ToDictionary(t => t.ParentTeam ?? new Team());
var current = teams.Single(t => t.ParentTeam == null);
do
yield return current;
while (lookup.TryGetValue(current, out current));
}
This produces the sequence in the reverse of the order you want, so put a Reverse on the end of the call if you want it in the other order:
Console.WriteLine(String.Join(" ", SortTeams(teams).Reverse().Select(t => t.Name)));
The "dummy" team is there because a dictionary does not allow a key to be null.
This is my suggestion:
public class Team
{
public Team ParentTeam;
public string Name;
int Level
{
get
{
int i = 0;
Team p = this.ParentTeam;
while (p != null)
{
i++;
p = p.ParentTeam;
}
return i;
}
}
static IEnumerable<Team> Sort(IEnumerable<Team> list)
{
return list.OrderBy(o => o.Level);
}
}
Of course, if there are Teams with equal level, you might use another criteria to sort them.
This should work:
static IEnumerable<Team> GetOrdered(IEnumerable<Team> teams)
{
var set = teams as HashSet<Team> ?? new HashSet<Team>(teams);
var current = teams.First(t => t.Parent == null);
while (set.Count > 1)
{
yield return current;
set.Remove(current);
current = set.First(t => t.Parent == current);
}
yield return set.Single();
}
This gives you the reversed order, so you should call Reverse() to get the order you are asking for.
We can find the ascendants of the null team, defining an extension
public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
{
Team t = l.FirstOrDefault(x =>
(x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
return new List<Team>() { t }.Concat(t != null ?
l.FindAscendants(t) : Enumerable.Empty<Team>());
}
and reverse the order of the null team's ascendants
list.FindAscendants(null).Reverse().Skip(1)
Edit
Alternative version of the extension with yield return
public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
{
Team t = l.FirstOrDefault(x =>
(x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
yield return t;
if (t != null)
foreach (Team r in l.FindAscendants(t))
{
yield return r;
}
}
Edit 2
In terms of the most efficient solution, a dictionary is the key.
As you can see now, there is no longer need to reverse the order.
So an optimized version would be
public static IEnumerable<Team> FindDescendandOptimized(this List<Team> l, Team from)
{
int count = l.Count;
var dic = l.ToDictionary(x => x.ParentTeam?.Name??"");
Team start = dic[from?.Name??""];
Team[] res = new Team[count];
res[count - 1] = start;
for (int i = count - 2; i >= 0; i--)
{
start = dic[start.Name];
res[i] = start;
}
return res;
}
with a test case and usage
List<Team> list = new List<Team>();
Team team = new Team();
team.Name = "0";
list.Add(team);
for (int i = 1; i < 200000; i++)
{
team = new Team();
team.Name = i.ToString();
team.ParentTeam = list.Last();
list.Add(team);
}
list.Reverse();
Console.WriteLine("Order List of " + list.Count +" teams");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
list.Shuffle();
Console.WriteLine("Shuffled List");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
DateTime start = DateTime.Now;
var res = list.FindDescendandOptimized(null);
list = res.ToList();
DateTime end = DateTime.Now;
Console.WriteLine("Reordered List");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
Console.WriteLine("Benchmark ms: " + (end - start).TotalMilliseconds);
Console.ReadLine();
where the test check is
static bool TestOrder(List<Team> list)
{
int tot = list.Count;
for (int i = 0; i < tot; i++)
{
if (!list[i].Name.Equals((tot-i-1).ToString()))
{
return false;
}
}
return true;
}
Edit 3
A final consideration, maybe obvious.
The absolutely most efficient way would have been to define a child team.
public class Team
{
public string Name;
public Team ParentTeam;
public Team ChildTeam;
}
appropriately filled like below
team.ParentTeam = list.Last();
list.Last().ChildTeam = team;
to enable an immediate reordering
DateTime start = DateTime.Now;
var res = list.OrderByChild(); //list.FindDescendandOptimized(null);
list = res.ToList();
DateTime end = DateTime.Now;
Console.WriteLine("Reordered List");
with a direct link
public static IEnumerable<Team> OrderByChild(this List<Team> l)
{
int count = l.Count;
Team start = l.First(x => x.ParentTeam == null);
Team[] res = new Team[count];
res[count - 1] = start;
for (int i = count - 2; i >= 0; i--)
{
start = start.ChildTeam;
res[i] = start;
}
return res;
}

C# reference array assignment issue

I'm having a little trouble reading values in from a database and assigning them to an array. It seem to work in my unit tests, but in practice some values are missing.
Here's my database code:
private void GetParameterValuesFromDatabase()
{
this.parameterValues = (from DataRow r in this.database.RunCommand("select * from KST_PARAM_VALUES v join DM_PARM_NAME p on v.PARM_NAME_KEY = p.PARM_NAME_KEY").Rows
where (int)r["SCENARIO_KEY"] == this.scenario.ScenarioKey
select new DatabaseParameter
{
ParameterValuesKey = r.Field<int>(0),
ProfileType = r.Field<string>(1),
ScenarioKey = r.Field<int>(2),
StressEditorKey = r.Field<int>(3),
StressClassKey = r.Field<int>(4),
PeriodKey = r.Field<int>(5),
ParameterNameKey = r.Field<int>(6),
ParameterValue = r.Field<double>(7),
ActiveStress = (r.Field<string>(8) == "Y") ? true : false,
ParameterKey = (int)r["PARM_NUMBER"]
}).ToDictionary(r => r.ParameterValuesKey, r => r);
}
Not having any issues with this part of my code, just showing for completeness.
private void LoadParameters()
{
this.GetParameterValuesFromDatabase();
// TODO: Assuming 9 periods for now, change to allow for variable periods
for (int i = 1; i <= MaxNumberOfStressPeriods; i++)
{
this.parametersByPeriod.Add(i, this.parameterValues.Where(t => t.Value.PeriodKey == i).ToDictionary(t => t.Key, t => t.Value));
}
Log.Instance.LogMessage(LogLevel.Debug, "Created parameter dictionaries from database");
// For every stress editor in the dictionary of stress editors
foreach (KeyValuePair<int, ClassList> ed in this.stressParams)
{
// For every type of class selector
foreach (ClassSelector c in Enum.GetValues(typeof(ClassSelector)))
{
// For each of the classes within each class list within the editor
for (int i = 0; i < ed.Value.ClassLists[c].Count; i++)
{
string className = ed.Value.ClassLists[c][i].Name;
// For each double array in each class
foreach (KeyValuePair<int, double[]> t in ed.Value.ClassLists[c][i].ClassVariables.EditorParameters)
{
double[] values = this.GetParameterValues(t.Key, ed.Key, className);
BasicStressEditorVariables.AddParameters(values, ed.Value, className, t.Key);
}
}
}
}
}
}
Above shows the overall LoadParameters() method.
Below we have some code that selects 9 values from the dictionary constructed from the database, ready to be added to the array.
private double[] GetParameterValues(int paramKey, int editorKey, string className)
{
double[] values = new double[9];
for (int i = 1; i <= MaxNumberOfStressPeriods; i++)
{
Dictionary<int, DatabaseParameter> temp = this.parametersByPeriod[i];
foreach (KeyValuePair<int, DatabaseParameter> d in temp)
{
if (d.Value.ParameterKey == paramKey && d.Value.PeriodKey == i && d.Value.StressEditorKey == editorKey && d.Value.ProfileType == className)
{
values[i - 1] = d.Value.ParameterValue;
}
}
}
return values;
}
Below shows getting the destination array from the dictionary, as indexes cannot be passed by reference
public static void AddParameters(double[] values, ClassList editor, string className, int paramKey)
{
// TODO: Maybe search all lists to eliminate the need for the class selector as a parameter
// TODO: Will throw an exception when nothing is found. Handle it
ParameterClass p = null;
foreach (ClassSelector c in Enum.GetValues(typeof(ClassSelector)))
{
p = editor.ClassLists[c].FirstOrDefault(f => f.Name == className);
if (p != null)
{
break;
}
}
// TODO: Notify that could not be found
if (p == null)
{
Log.Instance.LogMessage(LogLevel.Error, $"Unable to find class {className}");
return;
}
double[] dest = p.ClassVariables.editorParameters[paramKey];
AddParameterValues(values, ref dest);
}
And here's the AddParameterValues() method:
private static void AddParameterValues(double[] values, ref double[] destination)
{
if (values.Length != destination.Length)
{
return;
}
for (int i = 0; i < values.Length; i++)
{
destination[i] = values[i];
}
}
Debugging shows that some values are being loaded into the destination array, but some aren't. Could anyone tell me why this is? Or if not, point me toward some material?
Thank you for your time
I'm not that C# specialist but looking to following code as a C programmer
private double[] GetParameterValues(int paramKey, int editorKey, string className)
{
double[] values = new double[9];
//...
return values;
}
I would assume that the lifetime of values is only within the function GetParameterValues and the function GetParameterValues delivers the caller with reference to a dead variable.
What if you change the prototype to something like
private void GetParameterValues(ref double[] values, int paramKey, int editorKey, string className)

Is it possible to return dynamic objects or Dataset from a Sqlite Query?

I am using Sqlite.Net in my Xamarin.Forms application. So far it has been great at returning lists of objects if my object is a class like so:
SqliteDatabase.Connection.Query<Customer>("Select * from Customers");
I would now like to return the equivalent of a DataSet dynamically from my query
SqliteDatabase.Connection.Query("Select * from Customers inner join Calls on Customers.Id=Calls.CustomerId")
Now from the second query I would like to return a DataSet instead of a list of objects. I know I could create a new object which combines the columns of Customers and Calls but I don't want to have to create objects every time I want to query the database.
Is it possible to just dynamically return a Dataset or Object?
In the end I actually managed to come up with a method that will run any query and return the rows as items in the list and the columns as objects in the array:
public List<object[]> RunSql(string sqlString, bool includeColumnNamesAsFirstRow)
{
var lstRes = new List<object[]>();
SQLitePCL.sqlite3_stmt stQuery = null;
try
{
stQuery = SQLite3.Prepare2(fieldStrikeDatabase.Connection.Handle, sqlString);
var colLenght = SQLite3.ColumnCount(stQuery);
if (includeColumnNamesAsFirstRow)
{
var obj = new object[colLenght];
lstRes.Add(obj);
for (int i = 0; i < colLenght; i++)
{
obj[i] = SQLite3.ColumnName(stQuery, i);
}
}
while (SQLite3.Step(stQuery) == SQLite3.Result.Row)
{
var obj = new object[colLenght];
lstRes.Add(obj);
for (int i = 0; i < colLenght; i++)
{
var columnType = SQLitePCL.raw.sqlite3_column_decltype(stQuery, i);
switch (columnType)
{
case "text":
obj[i] = SQLite3.ColumnString(stQuery, i);
break;
case "int":
obj[i] = SQLite3.ColumnInt(stQuery, i);
break;
case "real":
obj[i] = SQLite3.ColumnDouble(stQuery, i);
break;
case "blob":
obj[i] = SQLite3.ColumnBlob(stQuery, i);
break;
case "null":
obj[i] = null;
break;
}
}
}
return lstRes;
}
catch (Exception)
{
return null;
}
finally
{
if (stQuery != null)
{
SQLite3.Finalize(stQuery);
}
}
}
SQLite.NET PCL is a .NET wrapper around sqlite.
Therefore you can query similar to EF by using a join in in LINQ or Lambda than in the Query. The wrapper will handle the conversion to sqlite query for you.
You can then return a new datatype of the joined type or a dynamic type.
Note : Joins are not directly supported in sqlite (more info) and work around is mentioned here.
Sample code:
var conn = new SQLiteConnection(sqlitePlatform, "foofoo");
var query = from customer in conn.Table<Customers>().ToList()
join call in conn.Table<Calls>().ToList()
on customer.ID equals call.CustomerId
select new { Customer = customer , Calls = call };
Lambda version:
conn.Table<Customer>().ToList().Join
(conn.Table<Call>().ToList(),
customer => customer.Id,
call => call.CustomerId,
(customer, call) => new { Customer = customer, Calls = call });
thank u so much user1! works perfect.
here is just an example how to use ur method:
var objects = mySQLiteConnection.RunSql("SELECT * FROM Persons", true);
// ColumnNames
List<string> ColumnNames = new List<string>();
for (int column = 0; column < objects[0].Length; column++)
{
if (objects[0][column] != null) spaltennamen.Add((string)objects[0][column]);
}
// RowValues
for (int row = 1; row < objects.Count; row++)
{
for (int column = 0; column < objects[row].Length; column++)
{
if (objects[row][column] != null) System.Diagnostics.Debug.WriteLine(spaltennamen[column] + " : " + objects[row][column]);
}
}
It sounds like what you want to do is essentially recreate ADO.NET. When you say "DataSet", I'm guessing that you are talking about ADO.NET. This probably means that you don't want to use the ORM functionality built in to the SQLite.Net library.
I have created this version of the library that will allow you to do flat table reads from an SQLite database. It means that you CAN read the data in to an ADO.NET dataset if you like.
https://github.com/MelbourneDeveloper/SQLite.Net.Standard
Unlike #Fabian Monkemoller, i was unable to get #User1's code to work straight away. This is a modified version that make use of nullable reference types and method-nesting to seperate the main-code from the try-catch block:
public static object?[][]? ToDataSet(this SQLiteConnection sqlConnection, string query , bool includeColumnNamesAsFirstRow = true)
{
var stQuery = SQLite3.Prepare2(sqlConnection.Handle, query );
var colLength = SQLite3.ColumnCount(stQuery);
try
{
return SelectRows().ToArray();
}
catch (Exception e)
{
return null;
}
finally
{
if (stQuery != null)
{
SQLite3.Finalize(stQuery);
}
}
IEnumerable<object?[]> SelectRows()
{
if (includeColumnNamesAsFirstRow)
{
yield return SelectColumnNames(stQuery, colLength).ToArray();
}
while (SQLite3.Step(stQuery) == SQLite3.Result.Row)
{
yield return SelectColumns(stQuery, colLength).ToArray();
}
static IEnumerable<object> SelectColumnNames(SQLitePCL.sqlite3_stmt stQuery, int colLength)
{
for (int i = 0; i < colLength; i++)
{
yield return SQLite3.ColumnName(stQuery, i);
}
}
static IEnumerable<object?> SelectColumns(SQLitePCL.sqlite3_stmt stQuery, int colLength)
{
for (int i = 0; i < colLength; i++)
{
var x = SQLitePCL.raw.sqlite3_column_decltype(stQuery, i);
yield return x switch
{
"text" => SQLite3.ColumnString(stQuery, i),
"integer" => SQLite3.ColumnInt(stQuery, i),
"bigint" => SQLite3.ColumnInt64(stQuery, i),
"real" => SQLite3.ColumnDouble(stQuery, i),
"blob" => SQLite3.ColumnBlob(stQuery, i),
"null" => null,
_ => throw new Exception($"Unexpected type encountered in for query {stQuery}")
};
}
}
}
}

Highlight cells of datagridview with different values in c#

I have two datagridview.
With same column headers but different cell data.
first One is called grid_db
second one is calld grid_statement.
If the value of grid_db is not same as that of grid_statement at cell[j] i must have the cells highlighted (red).
i tried the following
int no_of_col = grid_db.Columns.Count;
int j;
for (j = 0; j < no_of_col;)
{
//if statement value is null replace with ZERO
if (grid_statement.Rows[0].Cells[j].Value != null &&
!string.IsNullOrWhiteSpace(grid_statement.Rows[0].Cells[j].Value.ToString()))
{
B = grid_statement.Rows[0].Cells[j].Value.ToString();
}
//if db value is null replace with zero
if (grid_db.Rows[0].Cells[j].Value != null &&
!string.IsNullOrWhiteSpace(grid_db.Rows[0].Cells[j].Value.ToString()))
{
A = grid_db.Rows[0].Cells[j].Value.ToString();
}
if (A != B)
{
grid_db.Rows[0].Cells[j].Style.BackColor = Color.Red;
grid_statement.Rows[0].Cells[j].Style.BackColor = Color.Red;
j++;
}
}
But it does not works.The above codes highlights ALL the columns of both grids.
Help ?
I tried your code, and it works for me, the only thing i've changed is the for loop to increment on every pass, otherwise it can easily be infinite (it only works for 1st row because that's what your code does):
public Form1()
{
InitializeComponent();
grid_db.DataSource = new[]
{
new{
id = 1,
tekst="a"
},
new
{
id=2,
tekst="b"
}
}.ToList();
grid_statement.DataSource = new[]
{
new{
id = 1,
tekst="b"
},
new
{
id=2,
tekst="c"
}
}.ToList();
Load += (sender, args) =>
{
HighlightRows();
};
}
private void HighlightRows()
{
int no_of_col = grid_db.Columns.Count;
int j;
var B = "";
var A = "";
for (j = 0; j < no_of_col; j++)
{
//if statement value is null replace with ZERO
if (grid_statement.Rows[0].Cells[j].Value != null &&
!string.IsNullOrWhiteSpace(grid_statement.Rows[0].Cells[j].Value.ToString()))
{
B = grid_statement.Rows[0].Cells[j].Value.ToString();
}
//if db value is null replace with zero
if (grid_db.Rows[0].Cells[j].Value != null &&
!string.IsNullOrWhiteSpace(grid_db.Rows[0].Cells[j].Value.ToString()))
{
A = grid_db.Rows[0].Cells[j].Value.ToString();
}
if (A != B)
{
grid_db.Rows[0].Cells[j].Style.BackColor = Color.Red;
grid_statement.Rows[0].Cells[j].Style.BackColor = Color.Red;
}
}
}
var differentCells =
grid_db.Rows.OfType<DataGridViewRow>()
.SelectMany(r=>r.Cells.OfType<DataGridViewCell>())
.Where(c=>!grid_statement[c.ColumnIndex,c.RowIndex].Value.Equals(c.Value));
//loop through all the cells and make them red
foreach(var cell in differentCells)
cell.Style.BackColor = Color.Red;

Categories

Resources