Reputation: 1349
Assume we are given two lists (could be any primitive type):
var arguments = new List<String>() {"a", "b", "c", "d", "e", "f", "g", "h"};
var required = new List<String>() {"c", "g"};
All values of the required list have to be in the arguments list. If none are missing return null, else return a List of the missing arguments. Oh and the required values can not occur multiple times, if that's the case an exception should be thrown.
This is what I managed to put together:
private static IList<string> FindMissingRequiredValues(List<string> required, List<string> arguments)
{
var missing = new List<string>();
foreach (var r in required)
{
if (!arguments.Contains(r))
missing.Add(r);
}
if (missing.Count == 0)
missing = null;
return missing;
}
It works but it doesn't take care of duplicates, and it looks ugly. Is there an elegant solution that is generic (works for any primitive type) and takes care of duplicates , maybe with linq?
Again for clarification, the following conditions should be met:
1) If the one (or more) of the required values occur multiple times in the arguments list, thrown an error. This only accounts for required values, other values can occur multiple times in the arguments list.
2) It should work for any primitive datatype
3) Return null if all required values occur exactly once in the arguments list.
4) If some required values are missing in the arguments list return a list of the missing ones.
Upvotes: 0
Views: 699
Reputation: 16059
Here you want to check two conditions against lists.
If all required list elements are present in list, then return null
If there are any missing fields, then check missing list contains duplicate or not.
Here you go with your logic
// Except will give you list of elements from required list which are not present in arguments.
var missing = required.Except(arguments);
// to return null we will check that missing list contains any value or not
// if it is empty then retun null, which will satisfy your first condition
if(!missing.Any())
return null;
else if(isDuplicate(arguments, required)) //Check for duplicates
throw new Exception("Duplicate records");
else
return missing.Distinct().ToList(); //Else return distinct element from missing list.
As per your comment if you want to check duplicates in argument, then I added one extra function which will return boolean value
public static bool isDuplicate(List<string> arguments, List<string> required)
{
var group = arguments.GroupBy( i => i );
foreach( var grp in group )
{
if(required.Contains(grp.Key) && grp.Count() > 1)
return true;
}
return false;
}
POC : .Net Fiddle
Upvotes: 2
Reputation: 1245
If you need a generic solution I would go with something like this:
static List<T> FindMissingRequiredElements<T>(List<T> required, List<T> arguments)
{
// convert to Dictionary where we store the required item as a key against count for an item
var requiredDict = required.ToDictionary(k => k, v => 0);
foreach(var item in arguments)
{
if (requiredDict.ContainsKey(item)) // now we check each item in arguments
{
requiredDict[item]++; // if we have required, adding to count
if (requiredDict[item] > 1) // if we met required item more than once, throw exception
{
throw new Exception($"Required item {item} appeared more than once!");
}
}
}
var result = new List<T>();
// now we are checking for missing items
foreach (var key in requiredDict.Keys)
{
if (requiredDict[key] == 0)
{
result.Add(key);
}
}
return result.Any() // if there are missing items, return the list, if not - return null
? result
: null;
}
The key idea here is to use Dictionary to count how many occurrences of required items we have in the list.
Upvotes: 1
Reputation: 37377
Try this
if(required.GroupBy(i => i).Any(g => g.Count > 1))
throw new Exception("Duplicates found");
var missing = required.Except(arguments).ToList();
return missing.Lenght == 0 ? null : missing;
Upvotes: 1