Adding one linq line to my function calls - c#

I am trying to add a linq line to a MapField (Google protobuf).
My actual code works fine like
private static MapField<string, TestResultProto> dToP (Dictionary<Uri, TestResult> keyValuePairs)
{
MapField<string, TestResultProto> keyValues = new();
foreach (var pair in keyValuePairs)
{
keyValues[pair.key.ToString()] = TRTP (pair.value);
}
return keyValues;
}
private static TestResultProto TRTP (TestResult tr)
{
TestResultProto t = new()
{
id = tr.id;
email = tr.email;
};
return t;
}
And now I use the above methods.
object o = //something;
Class1 c = new Class1();
c.Results.Add(dToP(o.Results)); // This works as expected
//c.Results is a MapField<string, TestResultProto>
//o.Results is Dictionary<Uri, TestResult> keyValuePairs
So, to write the above line, I was thinking to add a linq like :
**c.Results.AddRange(o.Results.Select(TRTP))); //ERROR**
Here, I get the error in the linq (above line), saying no method for AddRange for MapField. So, there is something like AddEntriesFrom, but the signature is it expects Codec. Any idea on how to do it ?

If you're desperate to turn it LINQ you could do something like:
private static MapField<string, TestResultProto> dToP (Dictionary<Uri, TestResult> d)
{
var m = new MapField<string, TestResultProto>();
m.Add(d.ToDictionary(
kvp => kvp.Key.ToString(),
kvp => new TestResultProto
{
id = kvp.Value.id,
email = kvp.Value.email
}
));
return m;
}

Related

Why is the return is List<char>?

I am trying to pull file names that match the substring using "contains" method. However, return seem to be List<char> but I expect List<string>.
private void readAllAttribues()
{
using (var reader = new StreamReader(attribute_file))
{
//List<string> AllLines = new List<string>();
List<FileNameAttributeList> AllAttributes = new List<FileNameAttributeList>();
while (!reader.EndOfStream)
{
FileNameAttributeList Attributes = new FileNameAttributeList();
Attributes ImageAttributes = new Attributes();
Point XY = new Point();
string lineItem = reader.ReadLine();
//AllLines.Add(lineItem);
var values = lineItem.Split(',');
Attributes.ImageFileName = values[1];
XY.X = Convert.ToInt16(values[3]);
XY.Y = Convert.ToInt16(values[4]);
ImageAttributes.Location = XY;
ImageAttributes.Radius = Convert.ToInt16(values[5]);
ImageAttributes.Area = Convert.ToInt16(values[6]);
AllAttributes.Add(Attributes);
}
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
List<string>var unique_reference_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"ref")).FirstOrDefault().ImageFileName.ToList();
foreach (var unique_raw_filename in unique_raw_filenames)
{
var raw_attributes = AllAttributes.Where(x => x.ImageFileName == unique_raw_filename).ToList();
}
}
}
Datatype class
public class FileNameAttributeList
{ // Do not change the order
public string ImageFileName { get; set; }
public List<Attributes> Attributes { get; set; }
public FileNameAttributeList()
{
Attributes = new List<Attributes>();
}
}
Why is FirstOrDefault() does not work ? (It returns List<char> but I am expecting List<string> and fails.
The ToList() method converts collections that implement IEnumerable<SomeType> into lists.
Looking at the definition of String, you can see that it implements IEnumerable<Char>, and so ImageFileName.ToList() in the following code will return a List<char>.
AllAttributes.Where(x =>
x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
Although I'm guessing at what you want, it seems like you want to filter AllAttributes based on the ImageFileName, and then get a list of those file names. If that's the case, you can use something like this:
var unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).Select(y=>y.ImageFileName).ToList();
In your code
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
FirstOrDefault() returns the first, or default, FileNameAttributeList from the list AllAttributes where the ImageFileName contains the text non.
Calling ToList() on the ImageFileName then converts the string value into a list of chars because string is a collection of char.
I think that what you are intending can be achieved by switching out FirstOrDefault to Select. Select allows you to map one value onto another.
So your code could look like this instead.
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).Select(x => x.ImageFileName).ToList();
This then gives you a list of string.

Linq to Entities error

In MVVM, i am getting an entity set from the repository to viewmodel. While trying to access the elements of the entity, it throws an exception:
LINQ to Entities does not recognize the method 'InsurableRisk.Entities.QueriesParameter ElementAt[QueriesParameter](System.Linq.IQueryable`1[InsurableRisk.Entities.QueriesParameter], Int32)' method, and this method cannot be translated into a store expression.
Here is my code:
Repository:
public IQueryable<QueriesParameter> GetParams(int QKey)
{
IQueryable<QueriesParameter> param = (from Q in Context.QueriesParameters
where (Q.QueryKey == QKey)
select Q);
return param;
}
Service:
public IQueryable<QueriesParameter> GetParams(int QKey)
{
return _repository.GetParams(QKey);
}
ViewModel:
paramLabel = new string[] { "ParamLabel1", "ParamLabel2", "ParamLabel3", "ParamLabel4", "ParamLabel5", "ParamLabel6" };
param = new string[] { "Param1", "Param2", "Param3", "Param4", "Param5", "Param6" };
paramVisibility = new string[] { "ParamVisiblity1", "ParamVisiblity2", "ParamVisiblity3", "ParamVisiblity4", "ParamVisiblity5", "ParamVisiblity6" };
paramLabelVisibility = new string[] { "ParamLabelVisiblity1", "ParamLabelVisiblity2", "ParamLabelVisiblity3", "ParamLabelVisiblity4", "ParamLabelVisiblity5", "ParamLabelVisiblity6" };
private Dictionary<int, string> m_queryNames;
private Dictionary<int, string> m_ReadOnlyQueryNames;
private int m_SelectedQueryNames;
public int SelectedQueryNames
{
get
{
return m_SelectedQueryNames;
}
set
{
if (m_SelectedQueryNames != value)
{
m_SelectedQueryNames = value;
OnPropertyChanged("SelectedQueryNames");
var QKey = m_SelectedQueryNames;
var sqlQuery = _service.GetQuery(QKey);
var paramCount = _service.GetParamCount(QKey);
//code to make the run button visible and the parameters to be visible
m_Visibility = true;
IQueryable<QueriesParameter> param = _service.GetParams(QKey);
for (int i = 1; i <= paramCount; i++)
{
OnPropertyChanged(paramLabelVisibility[i]);
OnPropertyChanged(paramVisibility[i]);
QueriesParameter qParam = param.ElementAt(i); <!-- I get the exception here -->
m_LabelName = qParam.ParameterName;
OnPropertyChanged(paramLabel[i]);
//OnPropertyChanged(param[i]);
}
}
}
Any help as in why i am getting this error?
You are getting this error because ElementAt is not supported by LINQ to Entities (indeed, how you will translate it into SQL?). Here is list of Supported and Unsupported LINQ Methods.
You can enumerate over params instead:
IQueryable<QueriesParameter> param = _service.GetParams(QKey);
int i = 1; // btw why you are iterating from index 1? It should be zero!
foreach(var p in param)
{
OnPropertyChanged(paramLabelVisibility[i]);
OnPropertyChanged(paramVisibility[i]);
QueriesParameter qParam = p; // here
m_LabelName = qParam.ParameterName;
OnPropertyChanged(paramLabel[i]);
i++;
}
Another option - move query to client side by calling AsEnumerable() or ToList(). Then Linq to Objects will be used, where you can use ElementAt(index) method or via indexer [index]:
List<QueriesParameter> param = _service.GetParams(QKey).ToList();
//... your code
QueriesParameter qParam = param[i];

compare List<string> and List<T>

I'm using C# and framework 4.0.
I have a list of type string and another list of type class T;
How can I compare List with a List and save the difference?
private void simpleButton_Compare_Click(object sender, EventArgs e)
{
try
{
bool Is_Egal = true;
int i = 0;
foreach (string Od_Scan in Ordre_Scan)
{
if (!Outils.Get_Ordre_Donne()[i].NoOrdre.Contains(Od_Scan) && !String.IsNullOrWhiteSpace(Od_Scan))
{
Is_Egal = false;
Temp_Od_Scan.Add(Od_Scan);
}
i++;
}
foreach (Pers_Compare Od_Done in Outils.Get_Ordre_Donne())
{
if (!Ordre_Scan.Contains(Od_Done.NoOrdre) && !String.IsNullOrWhiteSpace(Od_Done.NoOrdre))
{
Is_Egal = false;
Temp_Od_Donne.Add(Od_Done);
}
else
{
Temp_Od_Donne_Egal.Add(Od_Done);
}
}
if (Is_Egal)
{
MessageBox.Show("égalité");
}
else
{
MessageBox.Show("PAS égalité");
}
}
catch (Exception excThrown)
{
MessageBox.Show(excThrown.Message);
}
}
and the data :
List<string> Ordre_Scan= new List<string> { "azer","qsdf"};
Pers_Compare obj = new Pers_Compare();
obj.Nolv = 1;
obj.Noordre = "qsdf"
Pers_Compare obj2 = new Pers_Compare();
obj2.Nolv = 1;
obj2.Noordre = "wxcv"
List<Pers_Compare> Ordre_Donne = new List<Pers_Compare>();
Ordre_Donne.add(obj);
Ordre_Donne.add(obj2);
And I want to save the data in Ordre_Donne but not in Od_Scan and vice versa.
foreach (string Od_Scan in Temp_Od_Scan)
{
all item that not found in List A
--> wxcv
}
foreach (var Od_Done in Temp_Od_Donne)
{
all item that not found in List B
--> azer
}
The answer given for a slightly different question (comparing a List with another List) seems to me to be a good solution for your issue, they address multiple issues to do with comparisons of lists.
EDIT: However you should be more specific with your requirements i.e. what exactly is a 'difference', e.g. is {1,1,2} and {1,2} the same?
Here is the answer given the most votes... (included here just encase it gets removed for some reason (as per Bob' suggestion))
"
DESCRIPTION:
I need to check that they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list. Is there a built-in function that checks this? What if I guarantee that each element appears only once in a list?
EDIT: Guys thanks for the answers but I forgot to add something, the number of occurrences of each element should be the same on both lists.
ANSWER:
If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Edit:
Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
var cnt = new Dictionary<T, int>();
foreach (T s in list1) {
if (cnt.ContainsKey(s)) {
cnt[s]++;
} else {
cnt.Add(s, 1);
}
}
foreach (T s in list2) {
if (cnt.ContainsKey(s)) {
cnt[s]--;
} else {
return false;
}
}
return cnt.Values.All(c => c == 0);
}
Edit 2:
To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...
"
var list1 = Ordre_Donne.Where(o => !Ordre_Scan.Any(s => s == o.Noordre));
var list2 = Ordre_Scan.Where(s => !Ordre_Donne.Any(o => o.Noordre == s));
You can either implement IComparable on your Pers_Compare class, which will look something like:
public int CompareTo(string other)
{
return this.Noordre.CompareTo(other);
}
Or, if you don't have control of the data structure, you could do something like
var Temp_Od_Donne = from od in Ordre_Donne
where !Ordre_Scan.Contains(od.Noordre)
select od;
var Temp_Od_Scan = from os in Ordre_Scan
where !Ordre_Donne.Select(od => od.Noordre).Contains(os)
select os;

Case insensitive group on multiple columns

Is there anyway to do a LINQ2SQL query doing something similar to this:
var result = source.GroupBy(a => new { a.Column1, a.Column2 });
or
var result = from s in source
group s by new { s.Column1, s.Column2 } into c
select new { Column1 = c.Key.Column1, Column2 = c.Key.Column2 };
but with ignoring the case of the contents of the grouped columns?
You can pass StringComparer.InvariantCultureIgnoreCase to the GroupBy extension method.
var result = source.GroupBy(a => new { a.Column1, a.Column2 },
StringComparer.InvariantCultureIgnoreCase);
Or you can use ToUpperInvariant on each field as suggested by Hamlet Hakobyan on comment. I recommend ToUpperInvariant or ToUpper rather than ToLower or ToLowerInvariant because it is optimized for programmatic comparison purpose.
I couldn't get NaveenBhat's solution to work, getting a compile error:
The type arguments for method
'System.Linq.Enumerable.GroupBy(System.Collections.Generic.IEnumerable,
System.Func,
System.Collections.Generic.IEqualityComparer)' cannot be
inferred from the usage. Try specifying the type arguments explicitly.
To make it work, I found it easiest and clearest to define a new class to store my key columns (GroupKey), then a separate class that implements IEqualityComparer (KeyComparer). I can then call
var result= source.GroupBy(r => new GroupKey(r), new KeyComparer());
The KeyComparer class does compare the strings with the InvariantCultureIgnoreCase comparer, so kudos to NaveenBhat for pointing me in the right direction.
Simplified versions of my classes:
private class GroupKey
{
public string Column1{ get; set; }
public string Column2{ get; set; }
public GroupKey(SourceObject r) {
this.Column1 = r.Column1;
this.Column2 = r.Column2;
}
}
private class KeyComparer: IEqualityComparer<GroupKey>
{
bool IEqualityComparer<GroupKey>.Equals(GroupKey x, GroupKey y)
{
if (!x.Column1.Equals(y.Column1,StringComparer.InvariantCultureIgnoreCase) return false;
if (!x.Column2.Equals(y.Column2,StringComparer.InvariantCultureIgnoreCase) return false;
return true;
//my actual code is more complex than this, more columns to compare
//and handles null strings, but you get the idea.
}
int IEqualityComparer<GroupKey>.GetHashCode(GroupKey obj)
{
return 0.GetHashCode() ; // forces calling Equals
//Note, it would be more efficient to do something like
//string hcode = Column1.ToLower() + Column2.ToLower();
//return hcode.GetHashCode();
//but my object is more complex than this simplified example
}
}
I had the same issue grouping by the values of DataRow objects from a Table, but I just used .ToString() on the DataRow object to get past the compiler issue, e.g.
MyTable.AsEnumerable().GroupBy(
dataRow => dataRow["Value"].ToString(),
StringComparer.InvariantCultureIgnoreCase)
instead of
MyTable.AsEnumerable().GroupBy(
dataRow => dataRow["Value"],
StringComparer.InvariantCultureIgnoreCase)
I've expanded on Bill B's answer to make things a little more dynamic and to avoid hardcoding the column properties in the GroupKey and IQualityComparer<>.
private class GroupKey
{
public List<string> Columns { get; } = new List<string>();
public GroupKey(params string[] columns)
{
foreach (var column in columns)
{
// Using 'ToUpperInvariant()' if user calls Distinct() after
// the grouping, matching strings with a different case will
// be dropped and not duplicated
Columns.Add(column.ToUpperInvariant());
}
}
}
private class KeyComparer : IEqualityComparer<GroupKey>
{
bool IEqualityComparer<GroupKey>.Equals(GroupKey x, GroupKey y)
{
for (var i = 0; i < x.Columns.Count; i++)
{
if (!x.Columns[i].Equals(y.Columns[i], StringComparison.OrdinalIgnoreCase)) return false;
}
return true;
}
int IEqualityComparer<GroupKey>.GetHashCode(GroupKey obj)
{
var hashcode = obj.Columns[0].GetHashCode();
for (var i = 1; i < obj.Columns.Count; i++)
{
var column = obj.Columns[i];
// *397 is normally generated by ReSharper to create more unique hash values
// So I added it here
// (do keep in mind that multiplying each hash code by the same prime is more prone to hash collisions than using a different prime initially)
hashcode = (hashcode * 397) ^ (column != null ? column.GetHashCode() : 0);
}
return hashcode;
}
}
Usage:
var result = source.GroupBy(r => new GroupKey(r.Column1, r.Column2, r.Column3), new KeyComparer());
This way, you can pass any number of columns into the GroupKey constructor.

How can I convert List<string> to List<myEnumType>?

I failed to convert List<string> to List<myEnumType>. I don't know why?
string Val = it.Current.Value.ToString(); // works well here
List<myEnumType> ValList = new List<myEnumType>(Val.Split(',')); // compile failed
Of cause myEnumType type defined as string enum type as this,
public enum myEnumType
{
strVal_1,
strVal_2,
strVal_3,
}
Is there anything wrong? Appreciated for you replies.
EDIT: Oops, I missed the C# 2 tag as well. I'll leave the other options available below, but:
In C# 2, you're probably best using List<T>.ConvertAll:
List<MyEnumType> enumList = stringList.ConvertAll(delegate(string x) {
return (MyEnumType) Enum.Parse(typeof(MyEnumType), x); });
or with Unconstrained Melody:
List<MyEnumType> enumList = stringList.ConvertAll(delegate(string x) {
return Enums.ParseName<MyEnumType>(x); });
Note that this does assume you really have a List<string> to start with, which is correct for your title but not for the body in your question. Fortunately there's an equivalent static Array.ConvertAll method which you'd have to use like this:
MyEnumType[] enumArray = Array.ConvertAll(stringArray, delegate (string x) {
return (MyEnumType) Enum.Parse(typeof(MyEnumType), x); });
Original answer
Two options:
Use Enum.Parse and a cast in a LINQ query:
var enumList = stringList
.Select(x => (MyEnumType) Enum.Parse(typeof(MyEnumType), x))
.ToList();
or
var enumList = stringList.Select(x => Enum.Parse(typeof(MyEnumType), x))
.Cast<MyEnumType>()
.ToList();
Use my Unconstrained Melody project:
var enumList = stringList.Select(x => Enums.ParseName<MyEnumType>(x))
.ToList();
In C# 2.0:
List<myEnumType> ValList = new List<myEnumType>();
foreach (string x in Val.Split(','))
ValList.Add((MyEnumType) Enum.Parse(typeof(MyEnumType), x));
List<String> list = new List<String>();
list.Add("strVal_1");
list.Add("strVal_2");
list.Add("strVal_3");
List<myEnumType> enumList = new List<myEnumType>();
foreach (var item in list)
{
enumList.Add((myEnumType)Enum.Parse(typeof(myEnumType), item));
}
Create an extension method and with Select do the Work:
public static class ExtensionClass
{
public static myEnumType GetEnumValue(this string input)
{
if (input == myEnumType.strVal_1.ToString())
return myEnumType.strVal_1;
return input == myEnumType.strVal_2.ToString() ? myEnumType.strVal_2 : myEnumType.strVal_3;
}
}
List<myEnumType> ValList = new List<myEnumType>(Val.Split(',').Select(p=>p.GetEnumValue()));
I missed c#2.0 tag :)
I added an extension method to IEnumerable<string> to do this for me. Skeet's answer is good, obviously, but it will throw an exception if the strings aren't valid for the enum (which you may or may not want), and it's a pretty ugly looking line.
public static class StringEnumerableExtensions {
public static IEnumerable<T> StringsToEnums<T>( this IEnumerable<string> strs) where T : struct, IConvertible {
Type t = typeof( T );
var ret = new List<T>();
if( t.IsEnum ) {
T outStr;
foreach( var str in strs ) {
if( Enum.TryParse( str, out outStr ) ) {
ret.Add( outStr );
}
}
}
return ret;
}
}
Given this enum:
public enum ColorEnum { Blue, Yellow }
You can use this like so:
var colors = new List<string>() {"Blue","Yellow","Black"};
var colorEnums = colors.StringsToEnums<ColorEnum>();
And you'll get a list with just Blue and Yellow.

Categories