How to do partial search on multiple keys dictionary - c#

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!

Related

How to deal with a System.InvalidOperationExcpetion in Dictionary's TryGetValue method

I'm doing this to look up for specific key inside the dictionary with the same row id_d i want to find and it really works great with keys that really exist, but it whatever throws me the exception when a key doesn't match the query criteria in the parameters value.
if(sentences.TryGetvalue(values = (from keys in sentences where keys.key.id_d == rows.id_d selesck keys.Key).First(), out listOf))
do you know how can i resolve this issue, i mean, just avoiding those null references from values and continuing without being stopped by the exception.
an example would be something like this:
if(sentences.TryGetValue(values = (from keys in sentences where keys.key.id_d == rows.id_d selesck keys.Key).First(), out listOf))
{
//do whatever
}
else
{
//if the 'value' query doesn't match the criteria, don't do anything
}
It's not clear why you're using a dictionary if the TKey isn't actually the key you're going to use to look up a value - you're missing out on the constant time lookup. I can only assume you're using it elsewhere.
If you want to find a value based on a key predicate, you can just select the value (rather than the key) and return FirstOrDefault - TryGetValue isn't required. This will return the default value (likely null, unless TValue is a value type) in the case that no match is found:
var result =
(from pair in sentences
where pair.Key.id_d == rows.id_d
select pair.Value).FirstOrDefault();
Though this might read better if you used method syntax throughout:
var result = sentences
.Where(x => x.Key == rows.id_d)
.Select(x => x.Value)
.FirstOrDefault();

Retrieving non-duplicates from 2 Collections using LINQ

Background: I have two Collections of different types of objects with different name properties (both strings). Objects in Collection1 have a field called Name, objects in Collection2 have a field called Field.
I needed to compare these 2 properties, and get items from Collection1 where there is not a match in Collection2 based on that string property (Collection1 will always have a greater or equal number of items. All items should have a matching item by Name/Field in Collection2 when finished).
The question: I've found answers using Lists and they have helped me a little(for what it's worth, I'm using Collections). I did find this answer which appears to be working for me, however I would like to convert what I've done from query syntax (if that's what it's called?) to a LINQ query. See below:
//Query for results. This code is what I'm specifically trying to convert.
var result = (from item in Collection1
where !Collection2.Any(x => x.ColumnName == item.FieldName)
select item).ToList();
//** Remove items in result from Collection1**
//...
I'm really not at all familiar with either syntax (working on it), but I think I generally understand what this is doing. I'm struggling trying to convert this to LINQ syntax though and I'd like to learn both of these options rather than some sort of nested loop.
End goal after I remove the query results from Collection1: Collection1.Count == Collection2 and the following is true for each item in the collection: ItemFromCollection1.Name == SomeItemFromCollection2.Field (if that makes sense...)
You can convert this to LINQ methods like this:
var result = Collection1.Where(item => !Collection2.Any(x => x.ColumnName == item.FieldName))
.ToList();
Your first query is the opposite of what you asked for. It's finding records that don't have an equivalent. The following will return all records in Collection1 where there is an equivalent:
var results=Collection1.Where(c1=>!Collection2.Any(c2=>c2.Field==c1.Name));
Please note that this isn't the fastest approach, especially if there is a large number of records in collection2. You can find ways of speeding it up through HashSets or Lookups.
if you want to get a list of non duplicate values to be retained then do the following.
List<string> listNonDup = new List<String>{"6","1","2","4","6","5","1"};
var singles = listNonDup.GroupBy(n => n)
.Where(g => g.Count() == 1)
.Select(g => g.Key).ToList();
Yields: 2, 4, 5
if you want a list of all the duplicate values then you can do the opposite
var duplicatesxx = listNonDup.GroupBy(s => s)
.SelectMany(g => g.Skip(1)).ToList();

Add on condition with Lambda

Is there a way I can do something like this with Lambda expressions?
responses.Add(sr).Where(v.Responses.TryGetValue(v.responseType, out sr));
I want to use lambda expressions or a ternary operator instead of a typical if expression.
NB:
responses is a List<string> type.
v.Responses is a Dictionary of <enum ResponseType, string>
v is some object
sr is a string.
What you want to do is:
string sr;
if (v.Responses.TryGetValue(v.responseType, out sr))
responses.Add(sr);
There is no way to ease the syntax and get the same performance.
But you could do:
responses.AddRange( v.Responses.Where( p => p.Key == v.responseType )
.Select( p => p.Value ) );
You may want to think about what the last one is doing, because it is kind of stupid...
EDIT: the reason why it is stupid is because the last expression translates into:
foreach(var pair in v.Responses)
{
if (pair.Key == v.responseType)
responses.Add(pair.Value);
}
So if your ResponseType enumeration had 6 million entries, the program would iterate over the entire set of keys to find the correct entry. In your case, since you already know the key, you should use v.Responses[key] as it is extremely fast (see in which cases dictionaries must be use).
LINQ is not supposed to modify collections.
Couldn't you simply do something like this:
string sr;
if(v.Responses.TryGetValue(v.responseType, out sr))
responses.Add(sr);
If I understand your question correctly, this might do what you're looking for.
Based on your example code, I'm assuming that your object "v" contains a field named "responseType" of type RepsonseType.
var responses = v.Responses
.Where(r => r.Key == v.responseType)
.Select(r => r.Value)
.ToList();

How to access the member of an IGrouping?

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);

Use Linq to filter Hashtable based on a property of Values custom object

I have a synchronized Hashtable with int as the key, and a custom class called Job as the value. I would like to filter this Hashtable based on a property in my Job class called JobSize. JobSize is just an enum with values Small, Medium, and Large.
It's fine if it needs to be converted to another collection type to do this.
I know there's a slick LINQy way to do this, but I haven't found it yet...
It looks like this will work for me:
var smallJobs = hashTable.Values.Cast<Job>().Where(job => job.JobSize == JobSize.Small);
The ".Cast<Job>()" is required because Hashtable is non-generic.
Do you need to maintain the keys and values in your filtered Hashtable? If so, try this.
It'll filter the Hashtable and return the filtered results as a strongly typed Dictionary<int,Job>:
var filtered = yourHashtable.Cast<DictionaryEntry>()
.Where(x => ((Job)x.Value).JobSize == JobSize.Small)
.ToDictionary(x => (int)x.Key, x => (Job)x.Value);
You should be able to use something like this:
IEnumerable<Job> smallJobs
= hashTable.Values.Cast<Job>.Where(job => job.JobSize == JobSize.Small);

Categories

Resources