I have a list of { string Key, IEnumerable<string> Values } items and want to transform it into a list of { string Key, string Value } items, such that the new list contains n items for each Key.
I want to use LINQ and had the idea to use SelectMany:
list.SelectMany(item => item.Values)
However, as you can see, I lose the Key with that transformation. What's the trick? I guess it should be easy and I am just missing the forest for the trees...
I also ran into this brain freeze. Adding the answer by #PetSerAl from the comments:
list.SelectMany(item => item.Values.DefaultIfEmpty(), (item, value) => Tuple.Create(item.Key, value))
Not the compiled code but this may answer your question
var finalList=list.SelectMany(item => item).Select(final => new{KeyName=item.Key,Coll=item.Values}).ToList();
Related
I am sure it has been answered somewhere before but for the love of god I cant find it.
I want to get a specific Value for a Key from a KeyValuePair List per LINQ one-liner.
My List: List<KeyValuePair<int, int>> LeagueKVPList
I think it goes something like this:
int x = LeagueKVPList.Where(v => v.Key.(int y)).Value
But that obviously does not work.
Thanks for any help.
You should use Select for that
var values = LeagueKVPList.Select(kvp => kvp.Value);
It returns you all values.
To get a single value you can use FirstOrDefault
var x = LeagueKVPList.FirstOrDefault(kvp => kvp.Key == y).Value;
I create a multiple keys dictionary as (registrantsFields is enumerable type)
var registrantsRepository = registrantsFields.ToDictionary(c => Tuple.Create(c.RegistrantID, c.FieldID, c.DataID));
I use ContainsKey to search the dictionary as
if (registrantsRepository.ContainsKey(Tuple.Create(registrantId, fieldId, dataId)))
So far it works fine.
But I want to search the dictionary with only 2 keys, i.e. what dictionary contains for certain registrantId and fieldId, but with any dataId. In other word, I like to find all items like
var entries = registrantsRepository(Tuple.Create(registrantId, fieldId, *))
How should it be done (perhaps in Linq)? Thanks.
I'd just create a separate Lookup.
var registrantsByIdAndField = registrantsFields
.ToLookup(r => Tuple.Create(c.RegistrantID, c.FieldID));
Then you still get fast lookups with this:
var entries = registrantsByIdAndField[Tuple.Create(registrantId, fieldId)];
There's no wildcard search like that, but there is a way to ignore those fields that you're not interested in when you're doing your search.
Iterate through the collection of Keys, referencing the properties of your Tuple that you're interested in matching on. You can do this using LINQ's Any method.
if (registrantsRepository.Keys.Any(x => x.Item1 == registrantId && x.Item2 == fieldId)
{
}
All good answers here. Is ToLookup an option for you?
https://msdn.microsoft.com/en-us/library/bb549073(v=vs.100).aspx
Edit: just realized #StriplingWarrior beat me to this!
In C#, I have an object type 'A' that contains a list of key value pairs.
The key value pairs is a category string and a value string.
To instantiate object type A, I would have to do the following:
List<KeyValuePair> keyValuePairs = new List<KeyValuePair>();
keyValuePairs.Add(new KeyValuePair<"Country", "U.S.A">());
keyValuePairs.Add(new KeyValuePair<"Name", "Mo">());
keyValuePairs.Add(new KeyValuePair<"Age", "33">());
A a = new A(keyValuePairs);
Eventually, I will have a List of A object types and I want to manipulate the list so that i only get unique values and I base it only on the country name. Therefore, I want the list to be reduced to only have ONE "Country", "U.S.A", even if it appears more than once.
I was looking into the linq Distinct, but it does not do what I want because it I can't define any parameters and because it doesn't seem to be able to catch two equivalent objects of type A. I know that I can override the "Equals" method, but it still doesn't solve the my problem, which is to render the list distinct based on ONE of the key value pairs.
To expand upon Karl Anderson's suggestion of using morelinq, if you're unable to (or don't want to) link to another dll for your project, I implemented this myself awhile ago:
public static IEnumerable<T> DistinctBy<T, U>(this IEnumerable<T> source, Func<T, U>selector)
{
var contained = new Dictionary<U, bool>();
foreach (var elem in source)
{
U selected = selector(elem);
bool has;
if (!contained.TryGetValue(selected, out has))
{
contained[selected] = true;
yield return elem;
}
}
}
Used as follows:
collection.DistinctBy(elem => elem.Property);
In versions of .NET that support it, you can use a HashSet<T> instead of a Dictionary<T, Bool>, since we don't really care what the value is so much as that it has already been hashed.
Check out the DistinctBy syntax in the morelinq project.
A a = new A(keyValuePairs);
a = a.DistinctBy(k => new { k.Key, k.Value }).ToList();
You need to select the distinct property first:
Because it's a list inside a list, you can use the SelectMany. The SelectMany will concat the results of subselections.
List<A> listOfA = new List<A>();
listOfA.SelectMany(a => a.KeyValuePairs
.Where(keyValue => keyValue.Key == "Country")
.Select(keyValue => keyValue.Value))
.Distinct();
This should be it. It will select all values where the key is "Country" and concat the lists. Final it will distinct the country's. Given that the property KeyValuePairs of the class A is at least a IEnumerable< KeyValuePair< string, string>>
var result = keyValuePairs.GroupBy(x => x.Key)
.SelectMany(g => g.Key == "Country" ? g.Distinct() : g);
You can use the groupby statement. From here you can do all kind off cool stuf
listOfA.GroupBy(i=>i.Value)
You can groupby the value and then sum all the keys or something other usefull
How can I do a "like" to find a dictionary key? I'm currently doing:
mydict.ContainsKey(keyName);
But some keyNames have an additional word appended (separated by a space), I'd like to do a "like" or .StartsWith(). The comparisons will look like this:
"key1" == "key1" //match
"key1" == "key1 someword" //partial match
I need to match in both cases.
You can use LINQ to do this.
Here are two examples:
bool anyStartsWith = mydict.Keys.Any(k => k.StartsWith("key1"))
bool anyContains = mydict.Keys.Any(k => k.Contains("key1"))
It is worth pointing out that this method will have worse performance than the .ContainsKey method, but depending on your needs, the performance hit will not be noticable.
mydict.Keys.Any(k => k.StartsWith("key1"));
While enumerating over the Keys you will lose the performance benefits of a dictionary:
mydict.ContainsKey(someKey); // O(1)
mydict.Keys.Any(k => k.StartsWith("key1")); // O(n)
If you run the .Contains() method of the string and not the dictionary you'll get what you want.
var matchingKeys = mydict.Keys.Where(x => x.Contains("key1"));
var keys = (from key mydict.keys where key.contains(keyName) select key).ToList();
The Keys member of the dictionary can be manipulated to check for presence with more complicated semantics than equality. It need not be a LINQ extension method check, but that is likely the simplest.
If you want the keys themselves and not a true/false you can do:
string match = "key1";
var matches = from k in mydict
where k.Key.Contains(match)
select new
{
k.Key
};
For matching and retrieving keys "like" you could use these extensions.
public static class Extensions
{
public static bool HasKeyLike<T>(this Dictionary<string, T> collection, string value)
{
var keysLikeCount = collection.Keys.Count(x => x.ToLower().Contains(value.ToLower()));
return keysLikeCount > 0;
}
public static List<string> GetKeysLike<T>(this Dictionary<string, T> collection, string value)
{
return collection.Keys.Select(x => x.ToLower().Contains(value.ToLower())).ToList();
}
}
The two lines below return an IGrouping<string, DataRow>:
var mbVals = GetMBValues_MBvsPU(mbRptDataPkg);
var puVals = GetPUValues_MBvsPU(puRptDataPkg);
I'd though that you could access the grouping's data like this mbVals[StringKey] but that doesn't look possible. I can do a foreach over the DataRows but it just seems to me one should be able to easily access through a linq expression somehow.
What I'd like to do is compare fields in the datarows from one with fields from datarows of the other through the keys.
Any thoughts?
Thanks!
IGrouping<> is IEnumerable<> so you use the ElementAt() extension method.
But for the situation you describe, you may be better off using Zip() to unify the two groups (if the items are in the same order and match exactly) or using Join() if they don't.
An instance that implements IGrouping<T, U> has a (one) key of type T. Since you want to compare based on keys (plural), an IGrouping<string, DataRow> isn't what you need.
You need an IEnumerable<IGrouping<string, DataRow>> or an ILookup<string, DataRow>. Something that has many keys.
ILookup<string, DataRow> source1 = GetSource1();
ILookup<string, DataRow> source2 = GetSource2();
var BothKeyed =
(
from key in source1.Select(g => g.Key).Union(source2.Select(g => g.Key))
select new
{
Key = key,
In1 = source1[key],//note, In1 may be empty.
In2 = source2[key] //note, In2 may be empty.
}
).ToLookup(x => x.Key);