KeyValuePair in Lambda expression - c#

I am trying to create a KeyValue pair collection with lambda expression.
Here is my class and below that my lambda code. I failed to create the KeyValuePair.
I want to get a collection of KeyValuePair of Id, IsReleased for the
comedy movies. I put those KeyValuePair in HashSet for quick search.
public class Movie{
public string Name{get;set;}
public int Id{get;set;}
public bool IsReleased{get;set;}
//etc
}
List<Movie> movieCollection=//getting from BL
var movieIdReleased= new
HashSet<KeyValuePair<int,bool>>(movieCollection.Where(mov=> mov.Type== "comedy")
.Select(new KeyValuePair<int,bool>(????));

You should pass lambda into that .Select method, not just expression:
.Select(movie => new KeyValuePair<int,bool>(movie.Id, movie.IsReleased))
hope that helps!

//.Select(new KeyValuePair<int,bool>(????));
.Select(movie => new KeyValuePair<int,bool>()
{ Key = movie.Id, Value = movie.IsReleased} );

var comedyMovies = movieCollection
.Where(mc => "comedy".Equals(mc.Type, StringComparison.OrdinalIgnoreCase))
.Select(mc => new KeyValuePair<int, bool>(mc.Id, mc.IsReleased));
var distinctComedyMovies = new HashSet<KeyValuePair<int,bool>>(comedyMovies);

Related

Removing specific values from a Dictionary C# with Linq

I have a dictionary which holds information from a parsed test run. The key is the name of the method and the value is a list of TestRunProperties. My dictionary contains all methods from a test run and I would like to remove the methods which failed during a test run. Is this possible to do with Linq?
TestRunProperties class:
public class TestRunProperties
{
public string computerName { get; set; }
public TimeSpan duration { get; set; }
public string startTime { get; set; }
public string endTime { get; set; }
public string testName { get; set; }
public string outcome { get; set; }
}
Dictionary:
//Key is the name of the method, value is the properties associated with each run
private static Dictionary<string, List<TestRunProperties>> runResults = new Dictionary<string, List<TestRunProperties>>();
I've tried this but I think I'm getting confused with the Where part:
runResults.Remove(runResults.Where(methodName => methodName.Value.Where(method => method.outcome.ToLower().Equals("failed"))));
I'm quite new to Linq and Lambda and I'm still trying to understand how to access data like this.
Just use a loop to remove the items you don't want. You can write an extension method to make it easier to call:
public static class DictionaryExt
{
public static void RemoveAll<K, V>(this IDictionary<K, V> dict, Func<K, V, bool> predicate)
{
foreach (var key in dict.Keys.ToArray().Where(key => predicate(key, dict[key])))
dict.Remove(key);
}
}
This usually will be more efficient than creating an entirely new dictionary, especially if the number of items being removed is relatively low compared to the size of the dictionary.
Your calling code would look like this:
runResults.RemoveAll((key, methodName) => methodName.Value.Where(method => method.outcome.ToLower().Equals("failed")));
(I chose the name RemoveAll() to match List.RemoveAll().)
You could create a new dictionary by filtering out the invalid ones:
var filtered = runResults.ToDictionary(p => p.Key, p => p.Value.Where(m => m.outcome.ToLower() != "failed").ToList());
Ok, grrrrrr was faster :-)
To be honest you're probably better off selecting a new dictionary from the existing one:
runResults.Select().ToDictionary(x => x.Key, x => x.Value.Where(x => x.Value.outcome != "failed"));
*editted to reflect list in the dictionary.
Actually, you can get rid of the ones with no successful results by doing this too:
runResults.Select(x => new { x.Key, x.Value.Where(x => x.Value.outcome != "failed")} ).Where(x => x.Value.Any()).ToDictionary(x => x.Key, x => x.Value);

Querying nested lists

I have two classes:
public GeneralClassName
{
public GeneralClassName ()
{
SpecificList = new List<OtherClass>();
}
public string StringValue;
public string OtherStringValue;
public List<OtherClass> SpecificList;
}
and
public OtherClass
{
public string Name;
public string Number;
}
After a JSON deserialization I obtain a nice List<GeneralClassName>, the result I want is a Dictionary<string, int> whose value is the sum of the variabiles "Number" inside List<OtherClass> inside List<GeneralClassName>, while the key is the variabile Name.
In other words I'd like to sum Number grouping by Name.
Now, the only thing that came across my mind is a nested foreach, something like that:
Dictionary<string, int> resultDictionary = new Dictionary<string, int>();
foreach(List<OtherClass> listOtherClass in bigListGeneralClass.Select(x => x.SpecificList))
{
foreach(OtherClass otherClass in listOtherClass)
{
int value = 0;
if(resultDictionary.ContainsKey(otherClass.Name))
{
resultDictionary[otherClass.Name] += otherClass.Number;
}
else
{
resultDictionary.Add(otherClass.Name, otherClass.Number);
}
}
}
While this solution seems to work well, I don't like it at all.
Is there a more clean way to find this result? Maybe through a nice LINQ query?
As you don't use any information from the GeneralClassName you can use SelectMany to flatten your list. This flat list of OtherClass instances is than grouped by the Name property. Finally, the list of groups is transformed into a dictionary with the key of the group (aka the Name property) being the key of the new property and the value being the sum of all Number values in that group:
var result = bigListGeneralClass.SelectMany(x => x.SpecificList)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key,
x => x.Sum(y => y.Number));
This code assumes that OtherClass.Number is in fact an int not a string. This assumption is also used in your sample code with the loop.
If this assumption is not correct, change y.Number to int.Parse(CultureInfo.InvariantCulture, y.Number).
Note: This will throw an exception if any of the numbers can't be parsed, so you might want to make sure beforehand that all contain valid numbers.
Try this:
Dictionary<string, int> result =
bigListGeneralClass.SpecificList.GroupBy(sl => sl.Name)
.ToDictionary(group => group.Key, group => group.Sum(x => Int32.Parse(x.Number)));

Convert Dictionary<string, object> to a collection of objects with key

Is there a way to convert Dictionary<string, obj> to collection of objects such that each single object in the collection includes the key as another property
Here is the class def for obj
class someclass
{
string property1;
string property2;
}
After conversion, I am expecting each object in the collection to be like
obj.property1
obj.property2
obj.Key
I have been struggling with this since along time and I seek some help. any ideas?
thanks in advance.
Something like
var myCollection = from de in myDictionary
select new
{
de.Value.property1,
de.Value.property2,
de.Key
}.ToList(); // or .ToArray()
should do the trick.
That will return a List of a new anonymous type with the properties you requested.
You could also(in addition to the anonymous type apporach) use a List<Tuple<string, string, string>>:
var list= dictionary
.Select(kv => Tuple.Create(kv.Value.property1, kv.Value.property2, kv.Key))
.ToList();
foreach(var item in list)
{
Console.WriteLine("property1:{0 property2:{1} key:{2}"
, item.Item1
, item.Item2
, item.Item3);
}
The advantage over an anonymous type is that you can return the Tuple easily from a method.
Edit: A third option(my favorite) is simply to create instances of a class that you've declared somewhere. That's the ideal way. I don't know why i thought that you want a class "on the fly".
class someOtherClass
{
public string property1{ get; set; };
public string property2{ get; set; };
public string Key{ get; set; };
}
List<someOtherClass> objects = dictionary
.Select(kv => new someOtherClass(){
property1 = kv.Value.property1,
property2 = kv.Value.property2,
Key = kv.Key
})
.ToList();
You may use anonymous type if you don't want to store the result like this:
In case you just wana use it as datasource for example.
var res = myDictionary.Select(pair => new { pair.Key, pair.Value.Property1, pair.Value.Property2 });
The other answers are good, so this is just a supplement.
You could use arrays of Length three:
var arrays = myDictionary
.Select(kv => new[] { kv.Value.property1, kv.Value.property2, kv.Key, });
Or you could write a new class
class SomeclassAndKey
{
public string property1;
public string property1;
public string Key;
}
and then say
var someclassAndKeys = myDictionary
.Select(kv => new SomeclassAndKey { property1 = kv.Value.property1, property2 = kv.Value.property2, Key = kv.Key, });
In each case you could append .ToList() if you wanted not to defer enumeration and get a full List<> out.

How to get values from IGrouping

I have a question about IGrouping and the Select() method.
Let's say I've got an IEnumerable<IGrouping<int, smth>> in this way:
var groups = list.GroupBy(x => x.ID);
where list is a List<smth>.
And now I need to pass values of each IGrouping to another list in some way:
foreach (var v in structure)
{
v.ListOfSmth = groups.Select(...); // <- ???
}
Can anybody suggest how to get the values (List<smth>) from an IGrouping<int, smth> in such a context?
Since IGrouping<TKey, TElement> implements IEnumerable<TElement>, you can use SelectMany to put all the IEnumerables back into one IEnumerable all together:
List<smth> list = new List<smth>();
IEnumerable<IGrouping<int, smth>> groups = list.GroupBy(x => x.id);
IEnumerable<smth> smths = groups.SelectMany(group => group);
List<smth> newList = smths.ToList();
Here's an example that builds/runs: https://dotnetfiddle.net/DyuaaP
Video commentary of this solution: https://youtu.be/6BsU1n1KTdo
foreach (var v in structure)
{
var group = groups.Single(g => g.Key == v. ??? );
v.ListOfSmth = group.ToList();
}
First you need to select the desired group. Then you can use the ToList method of on the group. The IGrouping is a IEnumerable of the values.
More clarified version of above answers:
IEnumerable<IGrouping<int, ClassA>> groups = list.GroupBy(x => x.PropertyIntOfClassA);
foreach (var groupingByClassA in groups)
{
int propertyIntOfClassA = groupingByClassA.Key;
//iterating through values
foreach (var classA in groupingByClassA)
{
int key = classA.PropertyIntOfClassA;
}
}
From definition of IGrouping :
IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
you can just iterate through elements like this:
IEnumerable<IGrouping<int, smth>> groups = list.GroupBy(x => x.ID)
foreach(IEnumerable<smth> element in groups)
{
//do something
}
var groups = list.GroupBy(x => x.ID);
Can anybody suggest how to get the values (List) from an IGrouping<int, smth> in such a context?
"IGrouping<int, smth> group" is actually an IEnumerable with a key, so you either:
iterate on the group or
use group.ToList() to convert it to a List
foreach (IGrouping<int, smth> group in groups)
{
var thisIsYourGroupKey = group.Key;
List<smth> list = group.ToList(); // or use directly group.foreach
}
If you have an IGrouping<GroupItem, ListItem>, and you want to access the items of type ListItem of this group without utilizing a foreach loop, it's very simple. The object of type IGrouping<GroupItem, ListItem> is of type IEnumerable<ListItem> as well, as it is defined as:
public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
So you can simply say:
foreach (IGrouping<GroupItem, ListItem> group in list.GroupBy(x => x.ID))
{
IEnumerable<ListItem> itemsInThisGroup = group;
// ...
}
If for some reason, it has to be a List<T> instead of an IEnumerable<T>, you can of course still call itemsInThisGroup.ToList(). But usually it's better not to if you needn't.
Simply do this:
// this will "split" the list into groups
var groups = list.GroupBy(x => x.ID);
// groups is a "collection of lists"
foreach (var sublist in groups)
{
// now the sublist is only a part of the original list
// to get which is the value of ID, you can use sublist.Key
}
You don't need Select().GroupBy(expr) makes "a list of lists", kind of.
Assume that you have MyPayments class like
public class Mypayment
{
public int year { get; set; }
public string month { get; set; }
public string price { get; set; }
public bool ispaid { get; set; }
}
and you have a list of MyPayments
public List<Mypayment> mypayments { get; set; }
and you want group the list by year. You can use linq like this:
List<List<Mypayment>> mypayments = (from IGrouping<int, Mypayment> item in yearGroup
let mypayments1 = (from _payment in UserProjects.mypayments
where _payment.year == item.Key
select _payment).ToList()
select mypayments1).ToList();

OrderBy with a String keySelector

I have the following function that is extracting me distinct values based on the properties of an object, here Client.
public List<DistinctValue> GetDistinctValues(string propertyName)
{
//how should I specify the keySelector ?
Func<string, object> keySelector = item => propertyName;
var list = new List<DistinctValue>();
var values = this.ObjectContext.Clients.Select(CreateSelectorExpression
(propertyName)).Distinct().OrderBy(keySelector);
int i = 0;
foreach (var value in values)
{
list.Add(new DistinctValue() { ID = i, Value = value });
i++;
}
return list;
}
private static Expression<Func<Client, string>> CreateSelectorExpression
(string propertyName)
{
var paramterExpression = Expression.Parameter(typeof(Client));
return (Expression<Func<Client, string>>)Expression.Lambda(
Expression.PropertyOrField(paramterExpression, propertyName),
paramterExpression);
}
public class DistinctValue
{
[Key]
public int ID { get; set; }
public string Value { get; set; }
}
I'm doing this because I do not know in before which property values I'll need to extract.
It's working, just the result is not sorted.
Can you please help me correct the sorting to make the OrderBy work as expected?
The properties are strings and I don't need to chain the sorting. I don't need to specify the sorting order either.
Thanks a lot in advance,
John.
Your keySelector currently returns the same string for each (the property name); and since LINQ is typically a stable sort, this results in no overall change. Since you have already projected to the string values, you can simply use a trivial x=>x mapping here:
var values = this.ObjectContext.Clients.Select(
CreateSelectorExpression(propertyName)).Distinct().OrderBy(x => x);
to order by the items themselves.
Thanks for the elegant solution. I further expanded upon the CreateSelectorExpression method so it can be leveraged outside of the Client class in the example above.
public static Expression<Func<T, string>> CreateSelectorExpression<T>(string propertyName)
{
var paramterExpression = Expression.Parameter(typeof(T));
return (Expression<Func<T, string>>)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName),
paramterExpression);
}
Usage
Func<IQueryable<YourEntity>, IOrderedQueryable<YourEntity>> orderBy = o => o.OrderByDescending(CreateSelectorExpression<YourEntity>("Entity Property Name"));

Categories