LINQ IEnumerable and List - c#

This function returns a dictionary with all key name values coming from an IEnumerable object. I want to filter out any items that do not exist in my list that I am passing in. I just want the properties that exist as a cols.Name
public static Dictionary<string, string> GetDataRowFromObject(IEnumerable<NameValue<string, object>> properties, List<ColDefModel> cols)
{
var dataRow = new Dictionary<string, string>();
foreach (NameValue<string, object> property in properties)
{
try
{
if (property.Value == null)
dataRow[property.Name] = "";
else
dataRow[property.Name] = property.Value.ToString();
}
catch (NullReferenceException e)
{
dataRow[property.Name] = "";
}
}
return dataRow;
}

var dictionary = properties.Where(nv => nv.Value != null)
.Where(nv => cols.Any(c => c.ColName == nv.Name))
.ToDictionary(nv => nv.Name, nv => nv.Value.ToString());
Hopefully this shows you why your method is unnecessary.

If I understand the question correctly, try doing your foreach through:
properties.Where(x => cols.Any(y => x.Name == y.Name))
instead of through properties. If performance is an issue, try making some sort of hashed list of the names in cols (like a HashSet); say it's called colNames, then change the above to x => colNames.ContainsKey(x.Name).

Related

C# GetDeclaredProperty Set Value error no overload for method SetValue has one argument

I have the below code in my method (actually an async static Task)
try
{
string responseValues = JSONtoKeyValue(responseBody);
Console.WriteLine("responseValues = " + responseValues);
//var dict = responseValues.Split('|').Select(x => x.Split('=')).ToDictionary(x => x[0], x => x[1]);
var dict = responseValues.Split('|')
.Select(x => x.Split('='))
.Where(x => x.Length > 1 && !String.IsNullOrEmpty(x[0].Trim())
&& !String.IsNullOrEmpty(x[1].Trim()))
.ToDictionary(x => x[0].Trim(), x => x[1].Trim());
Console.WriteLine("Dictionary created.");
Console.ReadLine();
foreach (KeyValuePair<string, string> entry in dict)
{
if (entry.Value == null)
{
dict.Remove(entry.Key);
}
else
{
string key = entry.Key;
string value = entry.Value;
if (cnsnt.GetType().GetTypeInfo().GetDeclaredProperty(key) != null)
{
cnsnt.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(value);
}
}
}
}
catch (System.Exception e)
{
Console.WriteLine(e.Message);
}
My problem is two-fold. First, I can't handle the duplicates for the dictionary (wanted to just keep the latest value if the key already exists) but even when I try with no duplicates, when I try to see if I have the property that the key has, I get error No overload for method SetValue has only one argument.
Any help would be appreciated immensely ..
Check the https://learn.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo.setvalue?view=netframework-4.8 set value documentation. There is indeed no such overload with one parameter.
Here is the easy way to update the dictionary value How to update the value stored in Dictionary in C#?

How to remove duplicates from object list based on that object property in c#

I've got a problem with removing duplicates at runtime from my list of object.
I would like to remove duplicates from my list of object and then set counter=counter+1 of base object.
public class MyObject
{
MyObject(string name)
{
this.counter = 0;
this.name = name;
}
public string name;
public int counter;
}
List<MyObject> objects_list = new List<MyObject>();
objects_list.Add(new MyObject("john"));
objects_list.Add(new MyObject("anna"));
objects_list.Add(new MyObject("john"));
foreach (MyObject my_object in objects_list)
{
foreach (MyObject my_second_object in objects_list)
{
if (my_object.name == my_second_object.name)
{
my_object.counter = my_object.counter + 1;
objects_list.remove(my_second_object);
}
}
}
It return an error, because objects_list is modified at runtime. How can I get this working?
With a help of Linq GroupBy we can combine duplicates in a single group and process it (i.e. return an item which represents all the duplicates):
List<MyObject> objects_list = ...
objects_list = objects_list
.GroupBy(item => item.name)
.Select(group => { // given a group of duplicates we
var item = group.First(); // - take the 1st item
item.counter = group.Sum(g => g.counter); // - update its counter
return item; // - and return it instead of group
})
.ToList();
The other answer seem to be correct, though I think it will do scan of the whole list twice, depending on your requirement this might or might not be good enough. Here is how you can do it in one go:
var dictionary = new Dictionary<string, MyObject>();
foreach(var obj in objects_list)
{
if(!dictionary.ContainsKey(obj.name)
{
dictionary[obj.name] = obj;
obj.counter++;
}
else
{
dictionary[obj.name].counter++;
}
}
Then dictionary.Values will contain your collection

How to compare key/value dictionary with == operator on a IReadOnlyCollection<string>?

I have a MultiValueDictionary<string, string> where I am trying to get a key by value.
var dic = na.prevNext; // Getter to get MultiValueDictionary
string nodePointingToThisOne = "";
foreach (var item in dic)
{
if(item.Value == "test")
{
nodePointingToThisOne = item.Key;
}
break;
}
This does not work so I tried Linq:
string nodePointingToThisOne = dic.Where(x => x.Value == this.nodeID).Select(x => x.Key);
But on both I get this error: Operator '==' cannot be applied to operands of type 'System.Collections.Generic.IReadOnlyCollection<string>' and 'string'
So my question is how do I make this comparison work for a read-only collection? I am aware that I get problems if a key exists multiple times but I reduced the problem to this one for now.
I read
Get Dictionary key by using the dictionary value
LINQ: Getting Keys for a given list of Values from Dictionary and vice versa
get dictionary key by value
Getting key of value of a generic Dictionary?
Get key from value - Dictionary<string, List<string>>
but they deal with a "normal" dictionary.
Since the Value property it self may contain multiple values you can't compare it directly against certain string using == operator. Use Contains() instead :
.....
if (item.Value.Contains("test"))
{
.....
}
...or in method chain version :
string nodePointingToThisOne = dic.Where(x => x.Value.Contains("test"))
.Select(x => x.Key)
.FirstOrDefault();
Try to iterate by Keys and then compare the value, and return the Key only if the Value matches.
Like this:
foreach (var key in dic.Keys)
{
if(dic[key].Contains("your value"))
return key;
}
You can iterate over keys like this
foreach (var key in dic.Keys)
{
if(key == "your key")
return key;
}
You can also iterate over values like this
foreach (var v in dic.Values)
{
if(v == "your value")
return v;
}
Example:
Dictionary<string, string> c = new Dictionary<string, string>();
c.Add("Pk", "Pakistan");
c.Add("Aus", "Australia");
c.Add("Ind", "India");
c.Add("Nz", "New Zeland");
c.Add("SA", "South Africa");
foreach (var v in c.Values)
{
if (v == "Australia")
{
Console.WriteLine("Your Value is = " + v);
// perform your task
}
}
foreach (var k in c.Keys)
{
if (k == "Aus")
{
// perform your task
Console.WriteLine("Your Key is = " + k);
}
}
Output:
Your Value is = "Australia"
Your Key is = "Aus"
If you look at the MultiValueDictionary, line 81 will give you a hint. It is:
[TestInitialize]
public void TestInitialize()
{
MultiValueDictionary = new MultiValueDictionary<TestKey, string>();
}
protected static void AssertAreEqual( IDictionary<TestKey, string[]> expected,
IMultiValueDictionary<TestKey, string> actual )
{
Assert.AreEqual( expected.Count, actual.Count );
foreach ( var k in expected.Keys )
{
var expectedValues = expected[ k ];
var actualValues = actual[ k ];
AssertAreEqual( expectedValues, actualValues );
}
}
So for your case, the solution is similar:
foreach (var item in dic.keys)
{
if(dict[item] == "test")
{
nodePointingToThisOne = item;
return nodePointingToThisOne;
}
}
worked for me:
foreach (int i in Dictionary.Keys)
{
if (Dictionary.Values.ToString() == "Your Value")
{
return Dictionary.Keys;
}
}

How could I convert these foreach loops into a LINQ-expression?

I used ReSharper to inspect the code issues in my project and it notified me that the following loop could be converted into a LINQ-expression:
var dictionary = new Dictionary<string, string[]>
{
{ "400", new[] { "12345", "54321", "51423" } },
{ "500", new[] { "67890", "09876", "63727" } },
{ "600", new[] { "41713", "98234", "96547" } },
{ "700", new[] { "00000", "67990", "83752" } }
};
// ...
var targetValue = "41713";
foreach (string group in dictionary.Keys)
{
foreach (string name in dictionary[group])
{
if (name == targetValue)
return group;
}
}
return "User";
The loop basically checks the dictionary's values (string arrays) to see if targetValue belongs to any of them and returns the key of that array if found inside.
I tried doing the following, but clearly it just returns the value inside if its value is equivalent to targetValue.
var r = dictionary
.SelectMany(t => t.Value)
.FirstOrDefault(t => t == targetValue);
So you want to get the first key in the dictionary which string[]-value contains a given value?
var pairs = dictionary.Where(kv => kv.Value.Contains(myValue));
if (pairs.Any())
{
string group = pairs.First().Key;
}
or less readable but a little bit more efficient since it executes the query only once:
var pair = dictionary.FirstOrDefault(kv => kv.Value.Contains(myValue));
if (!pair.Equals(default(KeyValuePair<string, string[]>)))
{
string group = pair.Key;
}
last but not least another approach which is my favorite and also uses the "User"-default:
string group = dictionary.Where(kv => kv.Value.Contains(myValue))
.Select(kv=> kv.Key)
.DefaultIfEmpty("User")
.First();
var r = dictionary.FirstOrDefault(
x => x.Value.FirstOrDefault(y => y == myValue) != null);
This will also get the desired value back or null if it does not exist:
EDIT:
var result = dictionary.SkipWhile(n => !n.Value.Contains(myValue)).FirstOrDefault().Key;
//another way to get the key
//var result = dictionary.SingleOrDefault(n => n.Value.Contains(myValue)).Key;
if (result != null)
{
//do whatever with the result variable here
}

Inserting value in List of values of a Key in Dictionary

I have a rowsDictionary that its keys point to a list of EmployeeSummary classes.
In those EmployeeSummary classes we also have a string property of Delivery_System
I am looping through this in this way but now stuck in the part that I want to have a deliverySystemFinder dictioanry that its keys are combinedKey as below and the value for each key is a list of distinct delivery_system values
//rowsDictionary is a Dictionary<string, List<EmployeeSummary>>
Dictionary<string, List<string>> deliverySystemFinder = new Dictionary<string, List<string>>();
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() + emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
// so now I should go and
//A) does deliverySystemFinder have this combinedKey? if not add it.
//B) Does combinedKey in the list of its values already have the value for delivery_system? if it does not then add it
}
}
This would work, for start:
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() +
emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
List<string> systems = null;
// check if the dictionary contains the list
if (!deliverySystemFinder.TryGetValue(combinedKey, out systems))
{
// if not, create it and add it
systems = new List<string>();
deliverySystemFinder[combinedKey] = systems;
}
// check if the list contains the value and add it
if (!systems.Contains(delivery_system))
systems.Add(delivery_system);
}
}
Now, a couple of remarks:
It doesn't make sense to iterate through Keys, and then do a lookup in each iteration. You can directly iterate KeyValuePairs using a foreach loop.
Using concatenated strings as unique keys often fails. In this case, what happens if you have users { LastName="Some", FirstName="Body" } and { LastName="So", FirstName="Mebody" } in your list?
Checking if a List contains a value is a O(n) operation. You would greatly improve performance if you used a HashSet<string> instead.
Finally, the simplest way to achieve what you're trying to do is to ditch those loops and simply use:
// returns a Dictionary<EmployeeSummary, List<string>>
// which maps each distinct EmployeeSummary into a list of
// distinct delivery systems
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s, new EmployeeSummaryEqualityComparer())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());
With EmployeeSummaryEqualityComparer defined something like:
class EmployeeSummaryEqualityComparer : IEqualityComparer<EmployeeSummary>
{
public bool Equals(EmployeeSummary x, EmployeeSummary y)
{
if (object.ReferenceEquals(x, null))
return object.ReferenceEquals(y, null);
return
x.FirstName == y.FirstName &&
x.LastName == y.LastName &&
... (depending on what constitutes 'equal' for you)
}
public int GetHashCode(EmployeeSummary x)
{
unchecked
{
var h = 31; // null checks might not be necessary?
h = h * 7 + (x.FirstName != null ? x.FirstName.GetHashCode() : 0);
h = h * 7 + (x.LastName != null ? x.LastName.GetHashCode() : 0);
... other properties similarly ...
return h;
}
}
}
If you really think that using the string key will work in all your cases, you can do it without the custom equality comparer:
// returns a Dictionary<string, List<string>>
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s.LastName.ToUpper() + s.FirstName.ToUpper())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());

Categories