visual c++ Sort List of Custom Class - sorting

I have a class student, defined in class_student.h like this:
#pragma once
using namespace System;
using System::Windows::Forms::MessageBox;
using namespace System::Collections::Generic;
using namespace MySql::Data::MySqlClient;
public ref class student {
public:
String^ user_name;
String^ my_class;
int weighted_grades;
int weighted_totals;
float average_percent;
int get_weighted_grades() {
String^ con_string = L"datasource=127.0.0.1;port=3306;username=root;password=1234;database=comp4;";
MySqlConnection^ con_database = gcnew MySqlConnection(con_string);
MySqlCommand^ get_grades = gcnew MySqlCommand("select coalesce(sum(method_marks) + sum(accuracy_marks), 0) from tbl_grades inner join tbl_test_instances on tbl_grades.test_instance=tbl_test_instances.id inner join tbl_students on tbl_test_instances.student_id = tbl_students.username inner join tbl_tests on tbl_test_instances.test_id=tbl_tests.test_id where tbl_students.class = tbl_tests.class and username='" + user_name + "';", con_database);
MySqlDataReader^ my_reader;
int grades;
try {
con_database->Open();
my_reader = get_grades->ExecuteReader();
my_reader->Read();
grades = my_reader->GetInt32(0);
}
catch (Exception^ ex) {
MessageBox::Show(ex->Message);
}
return grades;
}
int get_weighted_totals() {
String^ con_string = L"datasource=127.0.0.1;port=3306;username=root;password=1234;database=comp4;";
MySqlConnection^ con_database = gcnew MySqlConnection(con_string);
MySqlCommand^ get_totals = gcnew MySqlCommand("select coalesce(sum(method_marks) + sum(accuracy_marks), 0) from tbl_questions inner join tbl_test_questions on tbl_questions.question_id=tbl_test_questions.question_id inner join tbl_test_instances on tbl_test_questions.test_id=tbl_test_instances.test_id inner join tbl_students on tbl_test_instances.student_id=tbl_students.username inner join tbl_tests on tbl_test_instances.test_id=tbl_test_questions.test_id where tbl_students.class=tbl_tests.class and username='" + user_name + "';", con_database);
MySqlDataReader^ my_reader;
int totals;
try {
con_database->Open();
my_reader = get_totals->ExecuteReader();
my_reader->Read();
totals = my_reader->GetInt32(0);
}
catch (Exception^ ex) {
MessageBox::Show(ex->Message);
}
return totals;
}
student(String^ user_name, String^ txt_class) {
this->user_name = user_name;
this->my_class = txt_class;
this->weighted_grades = get_weighted_grades();
this->weighted_totals = get_weighted_totals();
this->average_percent = ((float)weighted_grades / (float)weighted_totals) * 100;
}
};
I have a List List<student^>^ students = gcnew List<student^>(); defined in another header file (with the class file included). This list has several instances of the class student added to it.
I would like to use students->Sort() or something of the like to sort through this list. I tried overwriting the operator<, both inside the class definition and outside it, but when I call Sort() I still get an error message saying that the items cannot be compared.
When trying to do this through the operator<, I used this, if that helps:
#pragma once
using namespace System;
using System::Windows::Forms::MessageBox;
using namespace System::Collections::Generic;
using namespace MySql::Data::MySqlClient;
public ref class student {
public:
String^ user_name;
String^ my_class;
int weighted_grades;
int weighted_totals;
float average_percent;
int get_weighted_grades() {
String^ con_string = L"datasource=127.0.0.1;port=3306;username=root;password=1234;database=comp4;";
MySqlConnection^ con_database = gcnew MySqlConnection(con_string);
MySqlCommand^ get_grades = gcnew MySqlCommand("select coalesce(sum(method_marks) + sum(accuracy_marks), 0) from tbl_grades inner join tbl_test_instances on tbl_grades.test_instance=tbl_test_instances.id inner join tbl_students on tbl_test_instances.student_id = tbl_students.username inner join tbl_tests on tbl_test_instances.test_id=tbl_tests.test_id where tbl_students.class = tbl_tests.class and username='" + user_name + "';", con_database);
MySqlDataReader^ my_reader;
int grades;
try {
con_database->Open();
my_reader = get_grades->ExecuteReader();
my_reader->Read();
grades = my_reader->GetInt32(0);
}
catch (Exception^ ex) {
MessageBox::Show(ex->Message);
}
return grades;
}
int get_weighted_totals() {
String^ con_string = L"datasource=127.0.0.1;port=3306;username=root;password=1234;database=comp4;";
MySqlConnection^ con_database = gcnew MySqlConnection(con_string);
MySqlCommand^ get_totals = gcnew MySqlCommand("select coalesce(sum(method_marks) + sum(accuracy_marks), 0) from tbl_questions inner join tbl_test_questions on tbl_questions.question_id=tbl_test_questions.question_id inner join tbl_test_instances on tbl_test_questions.test_id=tbl_test_instances.test_id inner join tbl_students on tbl_test_instances.student_id=tbl_students.username inner join tbl_tests on tbl_test_instances.test_id=tbl_test_questions.test_id where tbl_students.class=tbl_tests.class and username='" + user_name + "';", con_database);
MySqlDataReader^ my_reader;
int totals;
try {
con_database->Open();
my_reader = get_totals->ExecuteReader();
my_reader->Read();
totals = my_reader->GetInt32(0);
}
catch (Exception^ ex) {
MessageBox::Show(ex->Message);
}
return totals;
}
student(String^ user_name, String^ txt_class) {
this->user_name = user_name;
this->my_class = txt_class;
this->weighted_grades = get_weighted_grades();
this->weighted_totals = get_weighted_totals();
this->average_percent = ((float)weighted_grades / (float)weighted_totals) * 100;
}
bool operator<(student^ other) {
return this->average_percent > other->average_percent;
}
};
Or this:
class_student.h:
[class definition as shown at the top]
bool operator<(student^ s1, student^ s2);
class_student.cpp:
#include "class_student.h"
bool operator<(student^ s1, student^ s2) {
return s1->average_percent > s2->average_percent;
}

As the commenter noted, this is C++/CLI, not C++. If you want to write C++, I recommend that you create an actual C++ project. If you want to write managed code, I recommend that you create a C# project. C++/CLI is intended to serve as a bridge between managed languages (such as C#) and C++, and it generally should not be used for primary application development.
You need to implement interface IComparable<student^>^, rather than implementing an operator.
In .Net, operators cannot be specified in an interface, therefore the IComparable<T> interface specifies the method CompareTo(T), which returns -1 if this < other, 0 if equal, and 1 if greater.
For consistency and best practices, you should also override equals and hashCode, and implement IEquatable<T> as well.

Related

LINQ and removing duplicates from an array of objects

I'm trying to de-dupe an array of objects using two columns where the second column is a Dictionary. Best way to describe this is to show some code:
class MyClass
{
public int ID;
public Dictionary<int, int> Dict = new Dictionary<int, int>();
}
And now to create some objects:
List<MyClass> list = new List<MyClass>();
MyClass mc1 = new MyClass();
list.Add(mc1); mc1.ID = 1; mc1.Dict.Add(1, 1);
MyClass mc2 = new MyClass();
list.Add(mc2); mc2.ID = 1; mc2.Dict.Add(1, 1);
MyClass mc3 = new MyClass();
list.Add(mc3); mc3.ID = 1; mc3.Dict.Add(1, 2);
MyClass mc4 = new MyClass();
list.Add(mc4); mc4.ID = 2; mc4.Dict.Add(1, 1);
What I'm looking to accomplish is to distinct by ID and Dict. The results should look like this:
List of MyClass objects (not pretty)
1 //MyClass.ID
1,1 //MyClass.Dictionary
1
1,2
2
1,1
Notice that one of the objects was dropped from the original list because it had a duplicate ID and Dict (dictionary values). I've been playing around with alternate versions of:
var s = from p in list
group p by p.ID into group1
from group2 in
(from p in group1 group p by p.Dict)
group group2 by group1.Key;
but just haven't had any luck. Appreciate any insight folks might have on solving this problem.
PS - I'm not changing the rules but I believe a GROUP BY and a SELECTFIRST will be cleaner than a DISTINCT with its extra code for a Comparer. A pat on the back for anyone who can figure this out using GROUP BY.
For reference types you should add equality comparer in order to do what you want. Add the following class:
public class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass left, MyClass right)
{
if (left == null && right == null)
{
return true;
}
if (left == null || right == null)
{
return false;
}
if (left.ID == right.ID)
{
if (left.Dict == null && right.Dict == null)
{
return true;
}
if (left.Dict == null || right.Dict == null)
{
return false;
}
if (left.Dict.Count != right.Dict.Count)
{
return false;
}
foreach(var key in left.Dict.Keys)
{
if(!right.Dict.ContainsKey(key))
return false;
if (left.Dict[key] != right.Dict[key])
return false;
}
return true;
}
else return false;
}
public int GetHashCode(MyClass author)
{
return (author.ID).GetHashCode();
}
}
And use that comparer in Distinct override:
List<MyClass> list = new List<MyClass>();
MyClass mc1 = new MyClass();
list.Add(mc1); mc1.ID = 1; mc1.Dict.Add(1, 1);
MyClass mc2 = new MyClass();
list.Add(mc2); mc2.ID = 1; mc2.Dict.Add(1, 1);
MyClass mc3 = new MyClass();
list.Add(mc3); mc3.ID = 1; mc3.Dict.Add(1, 2);
MyClass mc4 = new MyClass();
list.Add(mc4); mc4.ID = 2; mc4.Dict.Add(1, 1);
var result = list.Distinct(new MyClassComparer()).ToList();
You should improve GetHashCode method. It will be your homework :)
Can I have half a pat for the following ? :)
var filteredList = list.GroupBy(mc => mc.ID)
.SelectMany(gr => gr.Distinct(new MyClassComparer()))
.ToList();
Comparer:
public class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass a, MyClass b)
{
return a.Dict.Count == b.Dict.Count && !a.Dict.Except(b.Dict).Any();
}
public int GetHashCode(MyClass a)
{
return a.ID;
}
}

How can I get the parameters from the WebAPI so that I can use them in the SQL query

So I've used this guide to setup a WebAPI that uses SQL:
http://www.codeproject.com/Articles/715073/Creating-a-Proof-of-Concept-Web-API-App-that-Uses
All is well that worked, but I noticed that this just pulls back ALL the records from SQL and does a "Where" in the C# instead, which is not ideal for my DB that is millions of rows.
My quick solution which I thought should work is to capture the parameter here:
public IEnumerable<ConfigMgrServerDB> GetByStateOrProvince(string StateOrProvince)
{
Debug.WriteLine(StateOrProvince);
APIparam = StateOrProvince;
return customerAddressData;
}
However even if I define APIparam in the scope of the class, when I try to use it in the method that calls SQL it apparently has no value, the parameter sent to SQL is set to 'default' like this:
,#param=default
Below is is the full code, that shows that the APIparam is defined a public string and that it can be set and echo'd out fine. However when i want to use it in the SQL code its null.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Web;
namespace WebApplication4.Models
{
public class ConfigMgrServerDBRepository : IConfigMgrServerDBRepository
{
private readonly List<ConfigMgrServerDB> customerAddressData = new List<ConfigMgrServerDB>();
public string APIparam ;
public string strProgramName;
public int GetCount()
{
return customerAddressData.Count;
}
public IEnumerable<ConfigMgrServerDB> GetByStateOrProvince(string StateOrProvince)
{
Debug.WriteLine(StateOrProvince);
APIparam = StateOrProvince;
return customerAddressData;
}
public IEnumerable<ConfigMgrServerDB> GetAll()
{
return customerAddressData;
}
public ConfigMgrServerDB Add(ConfigMgrServerDB item)
{
if (item == null)
{
throw new ArgumentNullException("item arg was null");
}
if (customerAddressData != null) customerAddressData.Add(item);
return item;
}
public ConfigMgrServerDBRepository()
{
using (var conn = new SqlConnection(
#"Data Source=RE8-PRIMB\PRIMB;Initial Catalog=PrimB;Persist Security Info=True;User ID=sa;Password=pass;"))
//#"Data Source=RE8-NWMMC1\NWMMC1;Initial Catalog=AdventureWorksLT2012;Persist Security Info=True;User ID=sa;Password=pass;"))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
#"CREATE TABLE #Package
(
PackageID NVARCHAR(8),
Name NVARCHAR(512),
ProgramName NVARCHAR(100),
CollectionID NVARCHAR(8),
)
CREATE CLUSTERED INDEX IDX_C_Package_CollectionID ON #Package(CollectionID)
INSERT INTO #Package
SELECT Pa.[PackageID],Pa.[Name],MAX(Pro.Name)ProgramName, A.CollectionID
FROM v_Package (nolock) Pa
INNER JOIN vSMS_Program (nolock) Pro ON Pro.PkgID = Pa.PackageID AND (ProgramFlags&0x00001000)/0x00001000 <> 1
INNER JOIN v_Advertisement (nolock) A ON A.PackageID = Pa.PackageID AND A.ProgramName = Pro.Name
WHERE Pa.PackageType = 0 AND Pro.Name <> '*' /*AND Pro.Name NOT LIKE '%Nomad%'*/
GROUP BY Pa.PackageID,Pa.Manufacturer,Pa.[Name],Pa.[Description], A.CollectionID
SELECT p.PackageID, p.ProgramName/*, p.Name, cm.Name MachineName,s.Hardware_ID0,mac.MAC_Addresses0*/
FROM #Package p
INNER JOIN CollectionMembers (nolock) cm ON cm.SiteID = p.CollectionID
INNER JOIN v_R_System_Valid (nolock) s ON s.ResourceID = cm.MachineID
INNER JOIN v_RA_System_MACAddresses (nolock) mac ON mac.ResourceID = s.ResourceID
WHERE cm.Name = #PackageID
OR mac.MAC_Addresses0 = '78:2B:CB:9B:56:F3'
GROUP BY p.PackageID,p.Name, p.ProgramName";
cmd.CommandType = CommandType.Text;
//cmd.Parameters.AddWithValue("#PackageID", APIparam );
cmd.Parameters.AddWithValue("#PackageID", "RE8-W7-1");
Debug.WriteLine(APIparam);
cmd.Connection.Open();
using (SqlDataReader dataReader = cmd.ExecuteReader())
{
while (dataReader != null && dataReader.Read())
{
int i = 0;
while (dataReader.Read())
{
i++;
APIparam = Convert.ToString(dataReader["PackageId"]);
strProgramName = Convert.ToString(dataReader["ProgramName"]);
Add(new ConfigMgrServerDB { PackageId = APIparam , ProgramName = strProgramName });
}
if (i == 0) /* No Rows */
{
//collectionMessage = "Cannot add Machine to collection " + CMCollectionID + " because no CM resource found with this serial number" + System.Environment.NewLine;
Debug.WriteLine("no rows ");
}
}
}
}
}
}
}
}

C#. Search array of strings for longest element

I want to know how I can get out everyone of the the longest persons if there are several with the same length?
If only one person is the longest, then it works fine and the longest person with it´s name will show in MessageBox. But if there are more than one who are the longest, this code will not work...
public partial class Form1 : Form
{
int[] längdArray = new int[5];
string[] namnArray = new string[5];
int namn = 0;
int längd = 0;
public Form1()
{
InitializeComponent();
}
private void btnVisa_Click(object sender, EventArgs e)
{
int längst = 0;
int längdvärdet = 0;
int längdindex = 0;
string name = textBox1.Text;
namnArray[namn] = name;
namn = namn + 1;
textBox1.Clear();
int centimeter = int.Parse(textBox2.Text);
längdArray[längd] = centimeter;
längd++;
textBox2.Clear();
listBox1.Items.Add(name + " " + centimeter + " centimeter ");
if (längd == 5)
{
btnVisa.Enabled = false;
foreach (int antalLängder in längdArray)
{
if (antalLängder > längst)
{
längst = antalLängder;
längdvärdet = längdindex;
}
längdindex++;
}
string test = namnArray[längdvärdet]
MessageBox.Show(" Längsta person är " + test + " som är " + längst + " centimeter lång ");
}
Define behavior you want your app to present when there is more than one person. Should all display, or any one, or other? Try to use object constructions, it's easier to operate on them. C# is an object-oriented language. Put name and length in one structure then use LINQ.

How can I post a list then access it in my controller?

I created a list property in my model like so
public virtual List<String> listOfDays { get; set; }
then I converted and stored it in the list like this:
for (int i = 0; i < 30 i++)
{
var enrollment = new Enrollment();
enrollment.StudentID = id;
enrollment.listOfDays = searchString.ToList();
db.Enrollments.Add(enrollment);
db.SaveChanges();
}
I put a breakpoint here... enrollment.listOfDays = searchString.ToList(); ... and all is well. I see that the conversion was performed and I can see the values in listOfDays.
So I thought I would find a column in my database called listOfDays since I'm doing code first but the property is not there.
Then I thought I'd try accessing it anyway like this...
var classdays = from e in db.Enrollments where e.StudentID == id select e.listOfDays;
var days = classdays.ToList();
//here I get an error message about this not being supported in Linq.
Questions:
Why do you think the property was not in the database?
How can I post this array to my model then access it in a controller?
Thanks for any help.
Thanks to Decker: http://forums.asp.net/members/Decker%20Dong%20-%20MSFT.aspx
Here’s how it works:
Using form collection here…
In [HttpPost]…
private void Update (FormCollection formCollection, int id)
for (int sc = 0; sc < theSelectedCourses.Count(); sc++)
{
var enrollment = new Enrollment();
enrollment.CourseID = Convert.ToInt32(theSelectedCourses[sc]);
enrollment.StudentID = id;
enrollment.listOfDays = formCollection["searchString"] ;//bind this as a string instead of a list or array.
Then in [HttpGet]…
private void PopulateAssignedenrolledData(Student student, int id)
{
var dayList = from e in db.Enrollments where e.StudentID == id select e;
var days = dayList.ToList();
if (days.Count > 0)
{
string dl = days.FirstOrDefault().listOfDays;
string[] listofdays = dl.Split(',');
ViewBag.classDay = listofdays.ToList();
}
Thanks to Decker: http://forums.asp.net/members/Decker%20Dong%20-%20MSFT.aspx
Here’s how it works:
Using form collection here…
In [HttpPost]…
private void Update (FormCollection formCollection, int id)
for (int sc = 0; sc < theSelectedCourses.Count(); sc++)
{
var enrollment = new Enrollment();
enrollment.CourseID = Convert.ToInt32(theSelectedCourses[sc]);
enrollment.StudentID = id;
enrollment.listOfDays = formCollection["searchString"] ;//bind this as a string instead of a list or array.
Then in [HttpGet]…
private void PopulateAssignedenrolledData(Student student, int id)
{
var dayList = from e in db.Enrollments where e.StudentID == id select e;
var days = dayList.ToList();
if (days.Count > 0)
{
string dl = days.FirstOrDefault().listOfDays;
string[] listofdays = dl.Split(',');
ViewBag.classDay = listofdays.ToList();
}

How To Project a Line Number Into Linq Query Results

How can I project the row number onto the linq query result set.
Instead of say:
field1, field2, field3
field1, field2, field3
I would like:
1, field1, field2, field3
2, field1, field2, field3
Here is my attempt at this:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank=i++,
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
Unfortunately, the "Rank=i++" line throws the following compile-time exception:
"An expression tree may not contain an assignment operator"
Well, the easiest way would be to do it at the client side rather than the database side, and use the overload of Select which provides an index as well:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new
{
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.AsEnumerable() // Client-side from here on
.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index + 1;
})
.ToList();
}
}
Ok, that did the trick. Thanks.
Here is my final code...
Server:
public List<Score> GetHighScores(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select s;
return query.ToList<Score>();
}
}
Client:
void hsc_LoadHighScoreCompleted(object sender, GetHighScoreCompletedEventArgs e)
{
ObservableCollection<Score> list = e.Result;
_listBox.ItemsSource = list.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index+=1
}).ToList();
}
You could also make just a slight adjustment to your original code to get it working. Word of caution, if you databind or access the object again, the Rank will increment each time. In those cases the top answer is better.
let Rank = i++
and
Rank.ToString()
Full code:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
let Rank = i++
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank.ToString(),
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
This solution worked for me.
http://www.dotnetfunda.com/articles/article1995-rownumber-simulation-in-linq.aspx
.Select((x, index) => new
{
SequentialNumber = index + 1
,FieldFoo = x.FieldFoo
}).ToList();
List<Emp> Lstemp = GetEmpList();
int Srno = 0;
var columns = from t in Lstemp
orderby t.Name
select new {
Row_number=++Srno,
EmpID = t.ID,
Name = t.Name,
City = t.City
};

Resources