What's the C# equivalence of Python's min/max - c#

What's C#'s equivalence of the following Python's min/max code:
pairs = [ (2,"dog"), (1, "cat"), (3, "dragon"), (1, "tiger") ]
# Returns the PAIR (not the number) that minimizes on pair[0]
min_pair = min(pairs, key=lambda pair:pair[0])
# this will return (1, 'cat'), NOT 1
It seems that C#'s Enumerable.Min is very close. But according to its MSDN doc, it always returns the minimizing VALUE (not the original object). Am I missing anything?
EDIT
Please note - I'm not inclined to achieve this by sorting first, since sorting (O(nlogn)) is computationally heavier than finding the minimum (O(n)).
Please also note - Dictionary is not a desired approach either. It cannot handle cases where there are duplicate keys - (1, "cat") and (1, "tiger").
More importantly, dictionary cannot handle cases where the items to be processed is a complex class. E.g., finding minimum over a list of animal objects, using age as the key:
class Animal
{
public string name;
public int age;
}

The BCL doesn't have a MinBy function, but it's easy to write one yourself.
public static T MinBy<T, C>(this IEnumerable<T> items, Func<T, C> projection) where C : IComparable<C> {
return items.Aggregate((acc, e) => projection(acc).CompareTo(projection(e)) <= 0 ? acc : e);
}
You may choose to write a more complex MinBy than me, in order to avoid re-evaluating the projection. In any case, once you have your MinBy function you can easily solve the problem:
var pairs = new[] {Tuple.Create(2,"dog"), Tuple.Create(1, "cat"), Tuple.Create(3, "dragon"), Tuple.Create(1, "tiger")};
var min_pair = pairs.MinBy(e => e.Item1);

Use
Dictionary<int, string> pairs = new Dictionary<int, string>()
{ {2,"dog"}, {1, "cat"}, {3, "dragon"} };
var min = pairs.OrderBy(x => x.Key).FirstOrDefault();
OR
int min = pairs.Keys.Min();
Dictionary<int, string> result
= new Dictionary<int, string>() { {min, pairs[min]} };

I would use
var min = pairs.OrderBy(x => x.FirstValue).FirstOrDefault();
and while I agree that sorting is heavier than finding the minimum value please note that this is not sorting the entire set. It is finding the first (or default) item in an ordered enumeration over the set - which is iterated lazily.
If you had
var min = pairs.OrderBy(x => x.FirstValue).ToList().FirstOrDefault();
then I'd agree - you are sorting your pairs and then taking the first. But LINQ is smarter than that, and you will not be sorting the set. You'll be taking the first from a potentially ordered but as yet unexecuted collection.
In addition to your point about Dictionary being unable to use a complex collection - say a list of Animal - how would you sort by an Animal? You can never sort by a complex object. You instead need to use the age of the animal as the key. Dictionary can do this very easily - in fact, the key of a Dictionary would never be the same as the value of a Dictionary, else what would be the point?
var animals = new List<Animal>();
// get some animals...
var animalictionary = animals.ToDictionary(a => a.Age);
// assuming the animals have distinct ages, else
var animalLookup = animals.ToLookup(a => a.Age);
foreach (var animalGroup in animalLookup)
{
var age = animalGroup.Key;
Console.WriteLine("All these animals are " + age);
foreach (Animal animal in animalGroup)
{
Console.WriteLine(animal.name);
}
}

EDIT
var minage = collection.Min( x => x.Age ); //for maxage replace Min by Max
var minAgeAnimals = collection.where(x=> x.age == minage);
foreach(Animal animal in minAgeAnimals )
Console.Writeline ( animal.Age.ToString() + " : " + animal.Name);
Prev. Answered before edit of question
Make use of dictonary object in C# and than do something like this does the same thing you want
int minimumKey = touchDictionary.Keys.Min();
string value = "";
touchDictionary.TryGetValue(minimumKey, out value))
Console.Writeline ( "min key pair is:-" + minimumKey.ToString() + " : " + value);
or
With the help of the linq its become to easy for you
var dictionary = new Dictionary<int, string>
{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"} };
var maxKey = dictionary.Max(x => x.Key);
var minkey = dictionary.Min(x => x.Key);

Related

How to merge n List<Dictionary<string, string>> into a Dictionary using Linq

I have tried to merge a list (dynamic) of dictionaries into one single list for a given object, using linq, I saw many questions similars to this one, however in their cases they always consider known number of dictionaries.
Well, this is my sctructure:
I have a query that returns a list objects like this:
public class MyDto
{
public Dictionary<string, string> JsonDictionary { get; set; }
public Guid SomeId1{ get; set; }
public Guid SomeId2{ get; set; }
}
using linq i am doing this:
var q = _uow.TableWithJson.GetAll()
.Include(a=> a.TableId1)
.Include(a=> a.TableAux)
.ThenInclude(b=> b.TableId2)
.Select(r => new MyDto
{
SomeId1 = r.tableId1.Id,
SomeId2 = r.tableId2.Id,
JsonDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(r.JsonContent)
});
At the end, I have a result like this;
{
SomeId1: xxxxx,
SomeId2: yyyyy,
JsonDictionary: [
{key: key1, value: value 1}
{key: key1, value: value 2}
{key: key1, value: value 3}
{key: key1, value: value 4}
],
},
{
SomeId1: xxxxx,
SomeId2: yyyyy,
JsonDictionary: [
{key: key4, value: value 4}
{key: key5, value: value 5}
{key: key6, value: value 6}
],
}, // many other objects with different ids and different dictionaries.
As you can see, in the above snippet SomeId1 and SomeId2 of both objects are the same. So, in this case I would like to group these objects merging the JsonContent field into just one dictionary.
Also tried use .GroupBy but i have not been able to merge this jsonContent as an agregation.
Help!!! Please! =) Hugs
This works if you want to do it this way... However, you did not provide how you wanted to handle conflicts with JsonDictionaries with the same key but different value. In this scenario, I just overrode previously declared values. You'd have to change this if you wanted different behavior.
IEnumerable<MyDto> list = new List<MyDto>(); // This is the stuff you parsed
var results = new Dictionary<Tuple<Guid, Guid>, MyDto>();
foreach (var item in list) {
var key = new Tuple<Guid, Guid>(item.SomeId1, item.SomeId2);
if (results.ContainsKey(key))
foreach (var entry in item.JsonDictionary)
results[key].JsonDictionary[entry.Key] = entry.Value;
else results[key] = item;
}
list = results.Values;
UPDATE:
I wrote it in Linq if you really want it. It's pretty inefficient, but I couldn't think of many other ways to do this easily. If you want efficiency, you should use the above example.
var results = list
.GroupBy(
x => new Tuple<Guid, Guid>(x.SomeId1, x.SomeId2),
(x, y) => new MyDto {
SomeId1 = x.Item1,
SomeId2 = x.Item2,
JsonDictionary = y
.SelectMany(z => z.JsonDictionary)
.ToLookup(z => z.Key, z => z.Value)
.ToDictionary(z => z.Key, z => z.First())
});
So quick idea, not sure if this can be totally done in linq natively.
// lets group all of our id's and I'm not going to use groupby because I'm not a fan.
var itemsById = new Dictionary<string, List<MyDto>>();
foreach(var item in q)
{
if(itemsById.ContainsKey(item.SomeId))
{
itemsById[item.SomeId].Add(item);
}
else
{
itemsById.Add(item.SomeId, new List<MyDto>());
itemsById[item.SomeId].Add(item);
}
}
so now we have dictionary of all of items by their ID.
var finalizedDtos = new List<MyDto>();
foreach(var entry in items)
{
var finalizedDto = new MyDto{ someId = entry.Key };
foreach(var innerDictionary in entry.value.JsonDictionary)
{
var finalizedDto = new MyDto {SomeId = entry.Key};
var allKeyValuePairs = entry.Value.SelectMany(c => c.JsonDictionary);
finalizedDto.JsonDictionary = allKeyValuePairs.ToDictionary(key => key.Key, value => value.Value);
finalizedDtos.Add(finalizedDto);
}
}
Not a whole lot of linq, but really for the nested structure I couldn't come up with a better plan

How do I access things returned by GroupBy

Ridiculously simple question that for the life of me I cant figure out. How do I 'get' at the values returned by GroupBy?
Take simple example below. I want to print out the first value that occurs more than once. Looking at the output in the watch window (image below) it sort of suggests that list3[0][0] might get at "one". But it gives me an error.
Note, I'm looking for the general solution - ie understanding what GroupBy returns.
Also, I would like to use the watch window to help me figure out for my self how I would access variables (as I find much of MSDN reference incomprehensible) - is this possible?
var list1 = new List<String>() {
"one", "two", "three", "one", "two"};
var list3 = list1
.GroupBy(x => x)
.Where(x => x.Count() > 1)
.ToList();
Console.WriteLine("list3[0][0]=" + list3[0][0]); //error
While the VS debugger shows you an "index" number because the underlying type is a collection, the grouping is exposed as an IGrouping<T> that does not have an indexer. If you just want the first item in the first group do:
Console.WriteLine("list3[0][0] =" + list3.First().First());
If you want to see all if the items you cam loop through the groupings:
int gi = 0, ii = 0;
foreach(var g in list3)
{
foreach(item i in g)
{
Console.WriteLine("list3[{0}][{1}] = {2}", gi, ii, i);
ii++;
}
gi++;
}
You are looking for the .Key property, as GroupBy returns an IEnumerable containing IGrouping elements.
If you look at the documentation of GroupBy you'll see it returns a IEnumerable<IGrouping<TKey, TSource>>.
IGrouping<TKey,TSource> has a single property Key and itself inherits IEnumerable<TElement>.
So you can enumerate over the list of items returned from a call to GroupBy and each element will have a Key property (which is whatever you grouped by) as well as enumerate each item (which will be the list of items grouped together)
Hopefully this demonstrates a bit clearer. Given a class:
public class Person
{
public string Name{get;set;}
public int Age{get;set;}
}
And a list:
var people = new List<Person>{
new Person{Name="Jamie",Age=35},
new Person{Name="Bob",Age=45},
new Person{Name="Fred",Age=35},
};
Grouping and enumerating as follows:
var groupedByAge = people.GroupBy(x => x.Age);
foreach(var item in groupedByAge)
{
Console.WriteLine("Age:{0}", item.Key);
foreach(var person in item)
{
Console.WriteLine("{0}",person.Name);
}
}
Gives this output:
Age:35
Jamie
Fred
Age:45
Bob
Live example: http://rextester.com/OWPR50756
GroupBy return an IEnumerable<IGrouping<TKey, TSource>> where each IGrouping<TKey, TElement> object contains a sequence of objects and a key it's not a Multidimensional Array which can be accessed by index [][].
To access the first element try this
static void Main(string[] args)
{
var list1 = new List<String>() {
"one", "two", "three", "one", "two"};
var list3 = list1
.GroupBy(x => x)
.Where(x => x.Count() > 1)
.ToList();
Console.WriteLine("list3[0][0]=" + list3[0].ToList()[0].ToString());
//OR Console.WriteLine("list3[0][0]=" + list3[0].First());
}

Count numbers in a List

In C# i have a List which contains numbers in string format. Which is the best way to count all this numbers? For example to say i have three time the number ten..
I mean in unix awk you can say something like
tempArray["5"] +=1
it is similar to a KeyValuePair but it is readonly.
Any fast and smart way?
Very easy with LINQ :
var occurrenciesByNumber = list.GroupBy(x => x)
.ToDictionary(x => x.Key, x.Count());
Of course, being your numbers represented as strings, this code does distinguish for instance between "001" and "1" even if conceptually are the same number.
To count numbers that have the same value, you could do for example:
var occurrenciesByNumber = list.GroupBy(x => int.Parse(x))
.ToDictionary(x => x.Key, x.Count());
(As noted in digEmAll's answer, I'm assuming you don't really care that they're numbers - everything here assumes that you wanted to treat them as strings.)
The simplest way to do this is to use LINQ:
var dictionary = values.GroupBy(x => x)
.ToDictionary(group => group.Key, group => group.Count());
You could build the dictionary yourself, like this:
var map = new Dictionary<string, int>();
foreach (string number in list)
{
int count;
// You'd normally want to check the return value, but in this case you
// don't care.
map.TryGetValue(number, out count);
map[number] = count + 1;
}
... but I prefer the conciseness of the LINQ approach :) It will be a bit less efficient, mind you - if that's a problem, I'd personally probably create a generic "counting" extension method:
public static Dictionary<T, int> GroupCount<T>(this IEnumerable<T> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
var map = new Dictionary<T, int>();
foreach (T value in source)
{
int count;
map.TryGetValue(number, out count);
map[number] = count + 1;
}
return map;
}
(You might want another overload accepting an IEqualityComparer<T>.) Having written this once, you can reuse it any time you need to get the counts for items:
var counts = list.GroupCount();

Most elegant way to convert string array into a dictionary of strings

Is there a built-in function for converting a string array into a dictionary of strings or do you need to do a loop here?
Assuming you're using .NET 3.5, you can turn any sequence (i.e. IEnumerable<T>) into a dictionary:
var dictionary = sequence.ToDictionary(item => item.Key,
item => item.Value)
where Key and Value are the appropriate properties you want to act as the key and value. You can specify just one projection which is used for the key, if the item itself is the value you want.
So for example, if you wanted to map the upper case version of each string to the original, you could use:
var dictionary = strings.ToDictionary(x => x.ToUpper());
In your case, what do you want the keys and values to be?
If you actually just want a set (which you can check to see if it contains a particular string, for example), you can use:
var words = new HashSet<string>(listOfStrings);
You can use LINQ to do this, but the question that Andrew asks should be answered first (what are your keys and values):
using System.Linq;
string[] myArray = new[] { "A", "B", "C" };
myArray.ToDictionary(key => key, value => value);
The result is a dictionary like this:
A -> A
B -> B
C -> C
IMO, When we say an Array we are talking about a list of values that we can get a value with calling its index (value => array[index]), So a correct dictionary is a dictionary with a key of index.
And with thanks to #John Skeet, the proper way to achieve that is:
var dictionary = array
.Select((v, i) => new {Key = i, Value = v})
.ToDictionary(o => o.Key, o => o.Value);
Another way is to use an extension method like this:
public static Dictionary<int, T> ToDictionary<T>(this IEnumerable<T> array)
{
return array
.Select((v, i) => new {Key = i, Value = v})
.ToDictionary(o => o.Key, o => o.Value);
}
If you need a dictionary without values, you might need a HashSet:
var hashset = new HashSet<string>(stringsArray);
What do you mean?
A dictionary is a hash, where keys map to values.
What are your keys and what are your values?
foreach(var entry in myStringArray)
myDictionary.Add(????, entry);
I'll assume that the question has to do with arrays where the keys and values alternate. I ran into this problem when trying to convert redis protocol to a dictionary.
private Dictionary<T, T> ListToDictionary<T>(IEnumerable<T> a)
{
var keys = a.Where((s, i) => i % 2 == 0);
var values = a.Where((s, i) => i % 2 == 1);
return keys
.Zip(values, (k, v) => new KeyValuePair<T, T>(k, v))
.ToDictionary(kv => kv.Key, kv => kv.Value);
}
Dictionary<int, string> dictionaryTest = new Dictionary<int, string>();
for (int i = 0; i < testArray.Length; i++)
{
dictionaryTest.Add(i, testArray[i]);
}
foreach (KeyValuePair<int, string> item in dictionaryTest)
{
Console.WriteLine("Array Position {0} and Position Value {1}",item.Key,item.Value.ToString());
}
The Question is not very clear, but Yes you can convert a string to Dictionary provided the string is delimited with some characters to support Dictionary<Key,Value> pair
So if a string is like a=first;b=second;c=third;d=fourth you can split it first based on ; then on = to create a Dictionary<string,string> the below extension method does the same
public static Dictionary<string, string> ToDictionary(this string stringData, char propertyDelimiter = ';', char keyValueDelimiter = '=')
{
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
Array.ForEach<string>(stringData.Split(propertyDelimiter), s =>
{
if(s != null && s.Length != 0)
keyValuePairs.Add(s.Split(keyValueDelimiter)[0], s.Split(keyValueDelimiter)[1]);
});
return keyValuePairs;
}
and can use it like
var myDictionary = "a=first;b=second;c=third;d=fourth".ToDictionary();
since the default parameter is ; & = for the extension method.
You can create a dictionary from an IEnumerable<T>, including an array, via:
var dictionary = myEnumerable.ToDictionary(element => element.Key,
element => element.Value)
where Key and Value are the key and value you want to store in each dictionary element. Available in .NET Framework 3.5+/.NET Core 1.0+/.NET 5.0+. Official documentation.
If you want the dictionary values to be the elements from the original enumerable:
var dictionary = myEnumerable.ToDictionary(element => element.Key)
If you only need high-performance set operations, you may be able to use:
var words = new HashSet<string>(listOfStrings);
In simple terms, the HashSet class can be thought of as a Dictionary<TKey,TValue> collection without values. Official documentation.
(Note that a 'sequence' in an entirely unrelated object.
Originally submitted an existing answer edit but it was rejected by the author so posting separately, including with links to the official Microsoft documentation.)

Overlay/Join two collections with Linq

I have the following scenario:
List 1 has 20 items of type TItem, List 2 has 5 items of the same type. List 1 already contains the items from List 2 but in a different state. I want to overwrite the 5 items in List 1 with the items from List 2.
I thought a join might work, but I want to overwrite the items in List 1, not join them together and have duplicates.
There is a unique key that can be used to find which items to overwrite in List 1 the key is of type int
You could use the built in Linq .Except() but it wants an IEqualityComparer so use a fluid version of .Except() instead.
Assuming an object with an integer key as you indicated:
public class Item
{
public int Key { get; set; }
public int Value { get; set; }
public override string ToString()
{
return String.Format("{{{0}:{1}}}", Key, Value);
}
}
The original list of objects can be merged with the changed one as follows:
IEnumerable<Item> original = new[] { 1, 2, 3, 4, 5 }.Select(x => new Item
{
Key = x,
Value = x
});
IEnumerable<Item> changed = new[] { 2, 3, 5 }.Select(x => new Item
{
Key = x,
Value = x * x
});
IEnumerable<Item> result = original.Except(changed, x => x.Key).Concat(changed);
result.ForEach(Console.WriteLine);
output:
{1:1}
{4:4}
{2:4}
{3:9}
{5:25}
LINQ isn't used to perform actual modifications to the underlying data sources; it's strictly a query language. You could, of course, do an outer join on List2 from List1 and select List2's entity if it's not null and List1's entity if it is, but that is going to give you an IEnumerable<> of the results; it won't actually modify the collection. You could do a ToList() on the result and assign it to List1, but that would change the reference; I don't know if that would affect the rest of your application.
Taking your question literally, in that you want to REPLACE the items in List1 with those from List2 if they exist, then you'll have to do that manually in a for loop over List1, checking for the existence of a corresponding entry in List2 and replacing the List1 entry by index with that from List2.
As Adam says, LINQ is about querying. However, you can create a new collection in the right way using Enumerable.Union. You'd need to create an appropriate IEqualityComparer though - it would be nice to have UnionBy. (Another one for MoreLINQ perhaps?)
Basically:
var list3 = list2.Union(list1, keyComparer);
Where keyComparer would be an implementation to compare the two keys. MiscUtil contains a ProjectionEqualityComparer which would make this slightly easier.
Alternatively, you could use DistinctBy from MoreLINQ after concatenation:
var list3 = list2.Concat(list1).DistinctBy(item => item.Key);
Here's a solution with GroupJoin.
List<string> source = new List<string>() { "1", "22", "333" };
List<string> modifications = new List<string>() { "4", "555"};
//alternate implementation
//List<string> result = source.GroupJoin(
// modifications,
// s => s.Length,
// m => m.Length,
// (s, g) => g.Any() ? g.First() : s
//).ToList();
List<string> result =
(
from s in source
join m in modifications
on s.Length equals m.Length into g
select g.Any() ? g.First() : s
).ToList();
foreach (string s in result)
Console.WriteLine(s);
Hmm, how about a re-usable extension method while I'm at it:
public static IEnumerable<T> UnionBy<T, U>
(
this IEnumerable<T> source,
IEnumerable<T> otherSource,
Func<T, U> selector
)
{
return source.GroupJoin(
otherSource,
selector,
selector,
(s, g) => g.Any() ? g.First() : s
);
}
Which is called by:
List<string> result = source
.UnionBy(modifications, s => s.Length)
.ToList();

Categories