Comparing IEnumerable string to SortedList string,string - c#

I ran into a problem I cannot solve. :-) I have to find all the values which contains a certain substring then I must get back the key and value pair. I had to implement a system where I had to make a SortedList, where the Albums is a class string is the key of course
Albums alb = new Albums();
SortedList<string, Albums> list1 = new SortedList<string, Albums>();
The Albums class looks like this:
public class Albums : IComparable
{
public string albname;
public string name1;
public string releasedate;
public Guid albumID;
public Albums()
{
albumID = new Guid();
}
public override string ToString()
{
return "Album: " + Albname + "\t" + Releasedate;
}
public Albums(string albname, string releasedate)
{
this.albname = albname;
this.releasedate = releasedate;
}
public string Name1
{
get { return name1; }
set { name1 = value; }
}
public string Albname
{
get { return albname; }
set { albname = value; }
}
public string Releasedate
{
get { return releasedate; }
set { releasedate = value; }
}
public int CompareTo(object obj)
{
if (obj is Albums)
{
Albums other = (Albums)obj;
return albname.CompareTo(other.albname);
}
if (obj is string)
{
string other = (string)obj;
return releasedate.CompareTo(releasedate);
}
else
{
return -999;
}
}
}
What I tried at last that I put the Albums into a LinkedList:
LinkedList<string> albm1 = new LinkedList<string>();
I did manage to find all the Albums that contains the substring using IEnumerable:
string albsearch = textBox16.Text;
IEnumerable<string> result = albm1.Where(s => s.Contains(albsearch));
BUT I do not know how to compare result to the Values of the SortedList. I also tried to create a new SortedList which contains the album in string:
SortedList<string, string> list2 = new SortedList<string, string>();
Any suggestion would be appreciated. Thanks in advance!

When you enumerate a SortedList, each item in the enumeration is a key/value pair.
I think what you want is:
Albums alb = new Albums();
SortedList<string, Albums> list1 = new SortedList<string, Albums>();
var foundItems = list1.Where(item => item.Key.Contains(albsearch));
Or, if you want to search in the Album:
var foundItems = list1.Where(item => item.Value.albname.Contains(albsearch));
Of course, you could search the name1 field rather than the album name, if that's what you want.
Each item returned is a key/value pair. More correctly, a KeyValuePair<string, Album>.

[Test]
public void Allbum_Search_Test()
{
var searchText = "TestA";
var list1 = new SortedList<string, Albums>
{
{"TestAlbum1", new Albums("TestAlbum1","SomeDate")},
{"TestAlbum2", new Albums("TestAlbum2","SomeDate")},
{"OtherAlbum2", new Albums("OtherAlbum","SomeDate")}
};
var results = list1.Where(pair => pair.Key.Contains(searchText));
Assert.That(results.Count(), Is.EqualTo(2));
}
On another note i would highly recommend
making the fields private
renaming Albums to Album
changing the ReleaseDate type to DateTime
renaming Albname to AlbumName

Related

How to create dictionary for students and multiple courses grades?

I am learning C# dictionary and I am confused how to do this
I have a Dictionary of students names. and I would like to assign another dictionary to that.
my input similar to this
Student1:
Math,9
Science,5
English,2
Student2:
Math,9
Science,10
English,7
I tried to create a class called Info and here is my code
public class Info
{
public string course { get; set; }
public int grade { get; set; }
public Info(string c, int g)
{
course = c;
grade = g;
}
internal IDictionary<string, Info> infoDic { get; private set; }
public void Set(string Sname, string course, int grade)
{
Student s = new Student(Sname);
var infor = new Info(course, grade);
infoDic = new Dictionary<string, Info>();
infoDic.Add(s.Name, infor);
//return infoDic;
}
public Dictionary<string, Info> retrieve (string name)
{
Student s = new Student(name);
return infoDic;
}
}
}
and here is another attempt:
I tried to make the class Info , and in the main I create the Dictionary and give the values, but the problem is I have lets say 3 courses and 10 students, and sometimes I need to retrieve only the math grades for all students.
How to improve the code to differentiate between the courses? Or how to make the course name as another key?
public class Info
{
public string course { get; set; }
public int grade { get; set; }
public Info(string c, int g)
{
course = c;
grade = g;
}
}
class Test
{
public static void Main(string[] args)
{
Dictionary<string, Info> information = new Dictionary<string, Info>();
Info i1 = new Info("math", 9);
information.Add("Student1", i1);
Info i2 = new Info("science", 11);
information.Add("Student1", i2);
Info i3 = new Info("math", 13);
information.Add("student2", i3);
foreach (KeyValuePair<string, Info> eee in information)
{
Console.WriteLine("{0}\t{1}\t{2}", eee.Key, eee.Value.type, eee.Value.count);
}
}
}
I need two methods one to set the values that user enters and the other to retrieve a certain course values when the user requires them
Is there Any suggestions?
Split the problem into separate concerns.
Use the Student and Info classes just to store data. Importantly, every Student holds a collection of his courses.
public class Student {
public Student(string name) {
Name = name;
Infos = new List<Info>();
}
public string Name {get; set;}
public ICollection<Info> Infos {get; set;}
}
public class Info {
public Info(string course, int grade) {
Course = course;
Grade = grade;
}
public string Course { get; set; }
public int Grade { get; set; }
}
Data access is handled by a different class StudentRepository.
The central dictionary is of type IDictionary<string, Student> with the Student name as key and hidden inside the repository.
using System.Linq;
public class StudentRepository {
public StudentRepository() {
_studentsByName = new Dictionary<string, Student>();
}
// keep data store private so we can change the implementation
private IDictionary<string, Student> _studentsByName {get; set;}
public void Add(Student student) {
if (_studentsByName.ContainsKey(student.Name)) {
throw new ArgumentException($"Student '{student.Name}' already stored.");
}
_studentsByName.Add(student.Name, student);
}
public Student Get(string studentName) {
if (_studentsByName.ContainsKey(studentName)) {
return _studentsByName[studentName];
}
throw new ArgumentException("No student '" + studentName + "' stored.");
}
// Find Grade for certain student and course
public int GetGrade(string studentName, string course) {
if (_studentsByName.ContainsKey(studentName)) {
var student = _studentsByName[studentName];
var courseInfo = student.Infos.FirstOrDefault(i => i.Course == course);
if (courseInfo != null) {
return courseInfo.Grade;
}
else {
throw new ArgumentException(
$"Student '{studentName}' did not take the course '{course}'.");
}
}
else {
throw new ArgumentException($"No student '{studentName}' found.");
}
}
// Get dictionary of all students that took a certain course. Key: student name
public IDictionary<string, Info> GetCoursesByStudentName(string course) {
// Use LINQ to retrieve the infos you need.
// Here I create a new dictionary with Student name as Key and
// the first matching course info found as value.
// (Students that did not take this course are not in this dictionary):
return _studentsByName
.Where(kvp => kvp.Value.Infos.Any(i => i.Course == course))
.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value.Infos.First(i => i.Course == course));
}
}
Usage example:
const string MathCourseName = "Math";
var Student1 = new Student("Alice");
Student1.Infos.Add(new Info(MathCourseName, 4));
var Student2 = new Student("Bob");
Student2.Infos.Add(new Info(MathCourseName, 2));
var Student3 = new Student("Cesar");
Student3.Infos.Add(new Info("English", 3));
var repository = new StudentRepository();
repository.Add(Student1);
repository.Add(Student2);
repository.Add(Student3);
foreach(var kvp in repository.GetCoursesByStudentName(MathCourseName)) {
Console.WriteLine(kvp.Key + ": " + kvp.Value.Course + " - " + kvp.Value.Grade);
}
var bobsMathGrade = repository.GetGrade("Bob", MathCourseName);
Console.WriteLine("Bobs math grade: " + bobsMathGrade);
C# Fiddle for this example
What i understood from your statement, you need two methods: One for setting user values and other for getting user's course value, i've slightly modified your code in order to fulfill you requirement, you can do like this:
public class Info
{
public string course { get; set; }
public int grade { get; set; }
public Info(string c, int g)
{
course = c;
grade = g;
}
}
public class Student
{
public Dictionary<string,Dictionary<string,int>> student { get; set; }
public Student()
{
student = new Dictionary<string, Dictionary<string, int>>();
}
/// <summary>
///
/// </summary>
public void SetValue(string studentName, Info info)
{
if (!student.ContainsKey(studentName))
{
Dictionary<string, int> stud_info = new Dictionary<string, int>();
stud_info.Add(info.course, info.grade);
student.Add(studentName, stud_info);
}
else
{
student[studentName].Add(info.course, info.grade);
}
}
public Dictionary<string,int> GetValue(string studentName, string course)
{
Dictionary<string, int> info = new Dictionary<string, int>();
if (student.ContainsKey(studentName))
{
if (student[studentName].ContainsKey(course))
{
int grade = 0;
if(student[studentName].TryGetValue(course, out grade))
{
info.Add(course, grade);
return info;
}
}
}
return info;
}
}
public class Program
{
public static void Main(string[] args)
{
Student student = new Student();
Info i1 = new Info("math", 9);
student.SetValue("Student1", i1);
Info i2 = new Info("science", 11);
student.SetValue("Student1",i2);
Info i3 = new Info("math", 13);
student.SetValue("Student2", i3);
Dictionary<string, int> value = student.GetValue("Student2", "math");
//Grade of math for student2
Console.WriteLine("Grade: {0}", value["math"]);
}
}

Updating Custom Class in List<T>

I am trying to update a List which is a List of Interfaces to concrete classes.
I add to the List each Market type i am interested in, for this Example these Markets are A and B
I loop over all the markets, (sample provided with 3 markets A B & C, we are only interested in A and B) And determine which is of interest to us.
Once found we pass this to an extraction method too do its work and create an instance of the Correct Market_ class type.
This all works fine, but when i try to update the list with the Updates it does not get reflected in the List.
Code below, any Suggestions?
Thanks
public class Test
{
public Test()
{
TheMarkets MarketsToUpdate = new TheMarkets();
List<SpecificCompanyMarket> lstMarks = new List<SpecificCompanyMarket>();
lstMarks.Add(new SpecificCompanyMarket(1234, "A", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(5874, "B", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(2224, "C", "Some HTML DATA HERE"));
foreach (var item in lstMarks)
{
if (MarketsToUpdate.IsMarketWeAreInterestedIn(item.MarketName))
{
ITheMarkets MarkToUpdate = ExtractMarketData(item);
var obj = MarketsToUpdate.MarketsWeAreInterestedIn.FirstOrDefault(x => x.MarketName() == "A");
if (obj != null)
{
obj = MarkToUpdate;
}
}
}
//Look At MarketsToUpdate Now and the item has not changed, still original values
//I was expecting to see the new values for the fields in A, not the default 0's
}
public ITheMarkets ExtractMarketData(SpecificCompanyMarket item)
{
ITheMarkets market = null;
if (item.MarketName.ToUpper() == "A")
{
Market_A marketType = new Market_A();
marketType.SomeValue1 = 123;
marketType.SomeValue2 = 158253;
market = marketType;
}
//Other Market extractions here
return market;
}
}
public class SpecificCompanyMarket
{
public int MarketId { get; set; }
public string MarketName { get; set; }
public string MarketDataHTML { get; set; }
public SpecificCompanyMarket(int MID, string MName, string MData)
{
MarketId = MID;
MarketName = MName;
MarketDataHTML = MData;
}
}
public class TheMarkets
{
public List<ITheMarkets> MarketsWeAreInterestedIn = new List<ITheMarkets>();
public TheMarkets()
{
Market_A A = new Market_A();
Market_B B = new Market_B();
MarketsWeAreInterestedIn.Add(A);
MarketsWeAreInterestedIn.Add(B);
}
public bool IsMarketWeAreInterestedIn(string strMarketName)
{
bool blnRetVal = false;
foreach (var item in MarketsWeAreInterestedIn)
{
if (item.MarketName().ToUpper().Trim().Equals(strMarketName.ToUpper().Trim()))
{
blnRetVal = true;
break;
}
}
return blnRetVal;
}
}
public interface ITheMarkets
{
string MarketName();
}
public class Market_A : ITheMarkets
{
public string LabelType { get; private set; }
public double SomeValue1 { get; set; }
public double SomeValue2 { get; set; }
public double SomeValue3 { get; set; }
public Market_A()
{
LabelType = "A";
}
public string MarketName()
{
return LabelType;
}
}
public class Market_B : ITheMarkets
{
public string LabelType { get; private set; }
public List<string> SomeList { get; set; }
public double SomeValue { get; set; }
public Market_B()
{
LabelType = "B";
}
public string MarketName()
{
return LabelType;
}
}
This is a short example to get you going. Loop through your list, find the object you want to update, create a new object of that type and then find the original objects index in the list and overwrite it in place. You are essentially just replacing the object in the list with a new one not mutating the existing one.
foreach (var item in lstMarks)
{
//your code to get an object with data to update
var yourObjectToUpdate = item.GetTheOneYouWant();
//make updates
yourObjectToUpdate.SomeProperty = "New Value";
int index = lstMarks.IndexOf(item);
lstMarks[index] = yourObjectToUpdate;
}
You are extracting an obj from marketWeAreInterestedIn list using LINQ's firstOrDefault extension. This is a new object and not a reference to the obj in that list. Therefore, no updates will be reflected in the object inside that list. Try using 'indexof'
You are not storing "list of interfaces" in your list. List<T> stores an array of pointers to objects that support T interface. Once you enumerate (with Linq in your case) your list, you copy a pointer from list, which is not associated with list itself in any way. It is just a pointer to your instance.
To do what you want, you will have to build new list while enumerating the original one, adding objects to it, according to your needs, so the second list will be based on the first one but with changes applied that you need.
You can also replace specific instance at specific index instead of building new list in your code, but to do this you will need to enumerate your list with for loop and know an index for each item:
list[index] = newvalue;
But there is a third solution to update list item directly by Proxying them. This is an example
class ItemProxy : T { public T Value { get; set; } }
var list = new List<ItemProxy<MyClass>>();
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
foreach(var item in list)
if(item // ...)
item.Value = new MyClass(); // done, pointer in the list is updated.
Third is the best case for perfomance, but it will be better to use this proxying class for something more than just proxying.

Access dictionary value inside linq method

I got the following structures:
public class Foo
{
public string Code { get; set; }
public string Description { get; set; }
public string Name { get; set; }
}
var fooMapping = GetFooMapping();
var fooObjects = GetFoos();
fooObjects.All(foo =>
{
Tuple<string, string> value = null;
if (fooMapping.TryGetValue(foo.Code, out value))
{
foo.Description = value.Item1;
foo.Name = value.Item2;
};
return true;
});
The GetFoos method returns an Enumerable of Foo objects that don't have all their properties set.
GetFooMapping returns an
IDictionary<string, Tuple<string, string>>
where TKey = Foo.Code and TValue.Item1 = Foo.Description and TValue.Item2 = Foo.Name.
While debugging, after running through the last lines of code I see that some of the properties weren't set even though the Foo.Code exists in the dictionary.
Am I missing something?
Instead of All you could use Select and assign the result back to fooObjects
fooObjects = fooObjects.Select(foo =>
{
Tuple<string, string> value = null;
if (fooMapping.TryGetValue(foo.Code, out value))
{
foo.Description = value.Item1;
foo.Name = value.Item2;
}
return foo;
});
The main issue that you are likely having is that All is iterating over an IEnumerable that is generating it's items. Then the next time you iterate fooObjects it generates them again and the changes in All are lost. Further this is not how Linq is meant to be used. All is meant for checking a predicate over the collection, not for modifying or projecting.
The other option is to make sure you are working with a list and just use a foreach
var fooObjects = GetFoos().ToList();
foreach(var foo in fooObjects)
{
Tuple<string, string> value = null;
if (fooMapping.TryGetValue(foo.Code, out value))
{
foo.Description = value.Item1;
foo.Name = value.Item2;
}
}

create querystring using lambda from List T

i am having some difficulties creating a proper select.
i have my custom class:
internal class classA{
internal string FieldName { get; set; }
internal string FieldValue { get; set; }
internal bool IncludeInChecksum { get; set; }
}
what i am trying to do is to build and concatinate a querystring using the class list from above.
for the following object:
List<classA> requestParams = ...
the query string should look like:
a=1&b=2&c=3
ordered by FieldName ascending
where IncludeInChecksum = true
FieldName = FieldValue
preMD5= requestParams
.Where(x=>x.IncludeInChecksum)
.OrderBy(y=>y.FieldName)
.Aggregate((i,j)=>(i.FieldName+ "=" +j.FieldValue));
this is where i am stuck.
thanks in advance
I will try to get all name=value strings with the help of the Select() method. And then convert the result to array. After that just use String.Join() method to get the desired result.
Join(String, String[]) concatenates all the elements of a string array, using the specified separator between each element.
var preMD5= requestParams
.Where(x => x.IncludeInChecksum)
.OrderBy(y => y.FieldName)
.Select(z => string.Format("{0}={1}", z.FieldName, z.FieldValue))
.ToArray();
preMD5 = string.Join("&", preMD5);
Aggregate aggregates values from different rows. You need to combine values from different fields. For this you use Select:
requestParms.Where(...).OrderBy(...).Select(f=>f.FieldName+"="+f.FieldValue)
This will return an IEnumerable of name=value strings. You can use string.Join to combine them into one string.
I know it's been answered,
but still I wanted to provide my solution.
I used a dedicated method to build the query string parameter,
and an extension method to concat it all.
Hope this helps.
public class classA
{
internal string FieldName { get; set; }
internal string FieldValue { get; set; }
internal bool IncludeInChecksum { get; set; }
public classA(string fieldName, string fieldValue, bool includeInChecksum)
{
this.FieldName = fieldName;
this.FieldValue = fieldValue;
this.IncludeInChecksum = includeInChecksum;
}
public string ToQueryString()
{
return string.Format("{0}={1}",
this.FieldName,
this.FieldValue);
}
public void Test()
{
var list = new List<classA> {
new classA("A", "1", true) ,
new classA("D", "4", true) ,
new classA("B", "2", false) ,
new classA("C", "3", true)
};
var result = list.
Where(o => o.IncludeInChecksum).
OrderBy(o => o.FieldName).
Select(o => o.ToQueryString()).
ToStringEx("&");
}
}
public static class ExtensionMethods
{
public static string ToStringEx<T>(this IEnumerable<T> items, string separetor = ",")
{
if (items == null)
{
return "null";
}
return string.Join(separetor, items.Select(o => o != null ? o.ToString() : "[null]").ToArray());
}
}

Distinct Value from Linq Query

I have the below class and linq query I am using to populate a grid!
The Title is the same for every row returned. What I am trying to do is populate mString with the distinct Title from the query so I can bind it to a seperate textblock.
I probably didnt need to show all the code, but maybe it will help. How can I show the distinct Title.
public class Items
{
public int Id { get; set; }
public string Details { get; set; }
public string Title { get; set; }
public int NewNumber { get; set; }
}
private ObservableCollection<Items> mItem = new ObservableCollection<Items>();
private string mString = string.Empty;
public string SpecTitle
{
get { return mString; }
}
public ObservableCollection<Items> GetItems
{
get { return mItem; }
}
Here is the linq query
var results = (from z in mContext.View
orderby z.ItemNumber ascending
where z.ItemId == mId
select new Items()
{
Id = z.ItemId,
Details = z.Details,
Title = z.ItemTitle,
NewNumber = z.ItemNumber
});
List<Items> mNewItems = results.ToList();
mItem.Clear();
mNewItems.ForEach(y => mItem.Add(y));
var titleList = mNewItems.Select(i => i.Title).Distinct().ToList();
Converting my comment into an answer:
just do Items.Select(x => x.Title).Distinct();.
There is an additional library called moreLinq https://code.google.com/p/morelinq/ that has an extenction distinctby that you can you to distinct based on the given key.
it would as simle as this
var results = (from z in mContext.View
orderby z.ItemNumber ascending
where z.ItemId == mId
select new Items()
{
Id = z.ItemId,
Details = z.Details,
Title = z.ItemTitle,
NewNumber = z.ItemNumber
}).DistinctBy(c=>c.Title).ToList();
You can implement your custom comparer for distinct:
public class ItemsComparer : IEqualityComparer<Items>
{
public bool Equals(Items x, Items y)
{
return x.Title == y.Title;
}
public int GetHashCode(Items obj)
{
return obj.Title.GetHashCode();
}
}
then just use
var titleList = mNewItems.Distinct(new ItemsComparer()).Select(t=>t.Items);

Categories

Resources