Using Dictionary Parameters as Optional Arguments when Calling Method - c#

I'm presently trying to use a dictionary values to name optional parameters when invoking a method. I'm not sure this is possible with c# but I do something similar with queries using dynamic SQL.
string[] dobArrayKey = {"dob: "};
string[] dobArrayValue = {txtDob.Text};
string[] ptntNumArrayKey = { "PatientID: " };
string[] ptntNumArrayValue = { txtOfficeMR.Text};
string[] nameArrayKey = { "FirstName: ", "LastName: " };
string[] nameArrayValue = { txtFirstname.Text, txtLastname.Text };
List<List<string>> searchResults = new List<List<string>>();
Dictionary<string[], string[]> searchCriteria = new Dictionary<string[], string[]>
{
{dobArrayKey,dobArrayValue}
,{ptntNumArrayKey,ptntNumArrayValue}
,{nameArrayKey,nameArrayValue}
};
foreach (var item in searchCriteria)
{
if (item.Value[0] != "" && item.Value[0] != null)
{
searchResults.Add(new List<string>());
for (int x = 0; x <= item.Key.Count(); x++)
{
string strJSON = doPatientSearch(Convert.ToInt32(au.UserID)
, Convert.ToInt32(Session["PracticeID"]), au.SessionID, item.Key[x].ToString() : item.Value[x].ToString() );
PatientSearchResponse ptLi = JsonConvert.DeserializeObject<PatientSearchResponse>(json2);
foreach (PatientList3 patient in ptLi.PatientList)
{
searchResults[x].Add(patient.PatientNumber);
}
}
}
}
public static string doPatientSearch(int UserID, int PracticeID, string SessionID, string PatientID = null,
,string first = null, string last = null, string dob = null, string social = null)
{
//search
}
My colleague suggested I change the method itself by removing all of the optional parameters and instead passing through a dictionary that contains all of the parameters and handling them inside the method.
I think that would work, but for curiosities sake I wanted to get some feedback and find out whether or not something like I'm attempting to do in the above code is possible.
If it is impossible but there is another way of achieving the desired outcome I'd love to see your suggestions.
Thank you in advance.

Pass an expression
Since the criteria are used post-hoc (i.e. by filtering a complete resultset), you can use LINQ to filter the results. For maximum flexibility, the caller can pass in an Expression to be used as a callback on each item to determine if it should be included.
To get a filtered resultset:
public IEnumerable<Patient> FindPatients(Func<Patient,bool> criteria)
{
return sourceData
.Where (criteria);
}
To return a single result:
public Patient FindPatient(Func<Patient,bool> criteria)
{
return sourceData
.Single(criteria);
}
The criteria expression is just a function that accepts a patient and returns a Boolean. The caller can write this any way desired, or insert it as a lambda expression.
var results = patients.FindPatients( p => p.LastName == "Doe" );
Or
var results = patients.FindPatients
(
p =>
p.LastName.Contains("Doe") &&
p.PracticeID == 12
);
Or
var singleResult = patients.FindPatient( p => p.UserID == 1);
As you can see, the caller can provide literally any criteria desired, and has the benefit of type safety and early binding. This is far superior to using a Dictionary which has neither.
Full example code:
class Patient
{
public int UserID { get; set; }
public int PracticeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DOB { get; set; }
public string Social { get; set; }
public override string ToString()
{
return string.Format("{0} {1} {2}", UserID, FirstName, LastName);
}
}
class PatientRepository
{
static private readonly List<Patient> sourceData = new List<Patient>
{
new Patient
{
UserID = 1, PracticeID = 10, FirstName = "John", LastName = "Doe", DOB = DateTime.Parse("1/2/1968"), Social="123456789"
},
new Patient
{
UserID = 2, PracticeID = 10, FirstName = "Jane", LastName = "Doe", DOB = DateTime.Parse("1/2/1958"), Social="123456790"
},
new Patient
{
UserID = 3, PracticeID = 10, FirstName = "John", LastName = "Carson", DOB = DateTime.Parse("4/1/1938"), Social="123456791"
}
};
public IEnumerable<Patient> FindPatients(Func<Patient,bool> criteria)
{
return sourceData
.Where (criteria);
}
public Patient FindPatient(Func<Patient,bool> criteria)
{
return sourceData
.Single(criteria);
}
}
public class Program
{
public static void Main()
{
//Get a reference to the data store
var patients = new PatientRepository();
Console.WriteLine("Multiple record search");
var results = patients.FindPatients
(
p => p.LastName == "Doe"
);
foreach (var p in results)
{
Console.WriteLine(p);
}
Console.WriteLine("Single record search");
var singleResult = patients.FindPatient
(
p => p.UserID == 1
);
Console.WriteLine(singleResult);
}
}
Output:
Multiple record search
1 John Doe
2 Jane Doe
Single record search
1 John Doe
See the working code on DotNetFiddle

Related

SearchString search operation with forename and surname

Hi I have one search box that takes searchstring and search it in the database. The input are like "utsav pal", "utsav p", "pal" for the account Name Utsav Pal. But I want to use only the initials like "uts pa". Can any one suggest me what can I do ?
Here is the logic I have used
if(!String.IsNullOrEmpty(vm.SearchString)){
accounts = accounts.where( a =>
((a.Forname != null) && (a.Forename +" "+ a.Surname).IndexOf(vm.SearchString, StringComparison.CurrentCultureIgnoreCase) >= 0 ) ||
((a.AccountCode != null) && a.AccountCode.IndexOf(vm.SearchString, StringComparison.CurrentCultureIgnoreCase) >= 0 )
You can use the logic below. I have created a Person model class and a repository class for the data based on the Person model.
The first code snippet is The Person Model and Repository Classes.
public class Person
{
public int Id { get; set; }
public string ForeName { get; set; }
public string SurName { get; set; }
public string Email { get; set; }
}
public static class Repository
{
public static List<Person> p { get; set; }
static Repository()
{
p = new List<Person>();
p.Add(new Person
{
Id = 1,
ForeName = "Nafi",
SurName = "Ahmadi",
Email = "Nafi#example.com"
});
p.Add(new Person
{
Id = 1,
ForeName = "Shafiq",
SurName = "Rahman",
Email = "Shafiq#example.com"
});
p.Add(new Person
{
Id = 1,
ForeName = "Wali",
SurName = "Mohammad",
Email = "Wali#example.com"
});
}
}
Here is the logic that uses initial letters to search in repository data.
static void Main(string[] args)
{
// Data set
List<Person> a = Repository.p;
// Search string
string SearchStringInput = "N A";
// Split search string
string[] SearchString = SearchStringInput.Split(" ");
if (!String.IsNullOrEmpty(SearchStringInput))
{
var p = a.Where(x =>
((x.ForeName != null)) && ((x.ForeName.ElementAt(0).ToString() == SearchString[0].ToString()) || x.ForeName.ToString() == SearchString[0].ToString()) && (x.SurName.ElementAt(0).ToString() == (SearchString[1].ToString())) || (x.SurName.ToString() == (SearchString[1].ToString())));
foreach (var item in p)
{
Console.WriteLine(item.Email);
}
}
Console.ReadKey();
}

Cartesian Product of Anonymous type

I am working on code which will give Cartesian product of two anonymous types. These 2 anonymous types are generated from database.
Code for 1st anonymous type:
private IEnumerable<object> GetItem()
{
return _unitOfWork.GetRepository<Item>()
.ListAll()
.Select(x => new
{
itemId = x.Id,
itemName = x.Name
})
}
Code for 2nd anonymous type:
private IEnumerable<object> GetVenue()
{
return _unitOfWork.GetRepository<Venue>()
.ListAll()
.Select(x => new
{
locationName = x.Address.City,
venueId = x.VenueId,
venueName = x.Name
})
}
I have following method to get the data and perform Cartesian product and return the data.
public object GetRestrictLookupInfo(IEnumerable<int> lookupCombinations)
{
IEnumerable<object> restrictList = new List<object>();
if (lookupCombinations.Contains(1))
{
var tempProductProfileList = GetItem();
restrictList = tempProductProfileList.AsEnumerable();
}
if (lookupCombinations.Contains(2))
{
var tempProductGroupList = GetVenue();
restrictList = (from a in restrictList.AsEnumerable()
from b in tempProductGroupList.AsEnumerable()
select new { a, b });
}
return restrictList;
}
I have controller which calls this method and return data in json format.
Controller Code
public HttpResponseMessage GetData(IEnumerable<int> lookupCombinations)
{
var lookupRestrictInfo = _sellerService.GetRestrictLookupInfo(lookupCombinations);
return Request.CreateResponse(HttpStatusCode.OK, lookupRestrictInfo);
}
Response expected is:-
[ {
"itemId": 1,
"itemName": "Music",
"locationName": "Paris",
"venueId": 99,
"venueName": "Royal Festival Hall"
} ]
Response which I receive is
[ {
"a": {
"itemId": 1,
"itemName": "Music"
},
"b": {
"locationName": "Paris",
"venueId": 99,
"venueName": "Royal Festival Hall" } }]
I am not able to get the expected JSON string.
You should start with the simplest possible code that shows your problem; your code above has a lot of complexities that may (or may not) have anything to do with your problem. Is this about manipulating anonymous types? Doing a Cartesian product with LINQ? Converting an object to JSON?
Here's one possible answer to what you might be looking for; notice that you can pass around anonymous types using generics instead of object.
namespace AnonymousTypes
{
class Program
{
static string Serialize(object o)
{
var d = (dynamic)o;
return d.ItemId.ToString() + d.ItemName + d.VenueId.ToString() + d.LocationName + d.VenueName;
}
static string GetData<T>(IEnumerable<T> result)
{
var retval = new StringBuilder();
foreach (var r in result)
retval.Append(Serialize(r));
return retval.ToString();
}
static string GetRestrictLookupInfo()
{
var restrictList = new[] { new { Id = 1, Name = "Music" }, new { Id = 2, Name = "TV" } };
var tempProductGroupList = new[] { new { LocationName = "Paris", Id = 99, Name = "Royal Festival Hall" } };
var result = from item in restrictList
from venue in tempProductGroupList
select new
{
ItemId = item.Id,
ItemName = item.Name,
LocationName = venue.LocationName,
VenueId = venue.Id,
VenueName = venue.Name
};
return GetData(result);
}
public static string GetData()
{
return GetRestrictLookupInfo();
}
static void Main(string[] args)
{
var result = GetData();
}
}
}
If that's not what you're looking for, you might start with code that doesn't use anonymous types, such as
namespace AnonymousTypes
{
sealed class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
sealed class Venue
{
public string LocationName { get; set; }
public int Id { get; set; }
public string Name { get; set; }
}
sealed class ItemAndVenue
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string LocationName { get; set; }
public int VenueId { get; set; }
public string VenueName { get; set; }
}
class Program
{
static IEnumerable<Item> GetItem()
{
return new[] { new Item { Id = 1, Name = "Music" } };
}
static IEnumerable<Venue> GetVenue()
{
return new[] { new Venue { LocationName = "Paris", Id = 99, Name = "Royal Festival Hall" } };
}
static IEnumerable<ItemAndVenue> GetRestrictLookupInfo()
{
var restrictList = GetItem();
var tempProductGroupList = GetVenue();
var result = from item in restrictList
from venue in tempProductGroupList
select new ItemAndVenue
{
ItemId = item.Id,
ItemName = item.Name,
LocationName = venue.LocationName,
VenueId = venue.Id,
VenueName = venue.Name
};
return result;
}
static string GetData()
{
var v = GetRestrictLookupInfo().First();
return v.ItemId.ToString() + v.ItemName + v.VenueId.ToString() + v.LocationName + v.VenueName;
}
static void Main(string[] args)
{
var result = GetData();
}
}
}
In order to produce a single item in the output you need to create a new type, named or anonymous. Since you are using objects rather than actual types, the quickest approach is to cast them to dynamic:
var tempProductGroupList = GetVenue();
restrictList = (from a in restrictList.Cast<dynamic>()
from b in tempProductGroupList.Cast<dynamic>()
select new {
itemId = (int)a.itemId,
itemName = (string)a.itemName,
locationName = (string)b.locationName,
venueId = (int)b.venueId,
venueName = (string)b.venueName
});
This code is tightly coupled to the code producing both lists, because it assumes the knowledge of the field names of types passed into it dynamically. Any change in the structure of source data must be followed by a change in the code making combinations. In addition, it defeats run-time checking, so you need to be very careful with this code.
Try to create a simple object instead of nesting:
select new { a.itemId, a.itemName, b.locationName }
Like an option:
public object GetRestrictLookupInfo(IEnumerable<int> lookupCombinations)
{
List<Dictionary<string, object>> result = new List<Dictionary<string, object>>();
if (lookupCombinations.Contains(1))
{
var tmp = _unitOfWork.GetRepository<Item>()
.ListAll()
.Select(x => new
{
itemId = x.Id,
itemName = x.Name
})
.Select(x =>
{
var dic = new Dictionary<string, object>();
dic.Add(nameof(x.itemId), x.itemId);
dic.Add(nameof(x.itemName), x.itemName);
return dic;
});
result.AddRange(tmp);
}
if (lookupCombinations.Contains(2))
{
var tmp = _unitOfWork.GetRepository<Venue>()
.ListAll()
.Select(x => new
{
locationName = x.Address.City,
venueId = x.VenueId,
venueName = x.Name
})
.Select(x =>
{
var dic = new Dictionary<string, object>();
dic.Add(nameof(x.locationName), x.locationName);
dic.Add(nameof(x.venueId), x.venueId);
dic.Add(nameof(x.venueName), x.venueName);
return dic;
});
result = result.SelectMany(r => tmp.Select(t => r.Concat(t)));
}
return result;
}
It looks like some magic. I uses dictionary instead of object. It can be make in more clear way (extract few methods), but the idea should be clear.
Then, during serialization it will be presented as you need.

Left join Lists with Linq and detect multiple matches

My goal is to compare to lists and update values on list A with values from list B. Along with that, I want it to work like a left join and keep all values from list A even if they didn't get updated and detect multiple matches.
What I have tried so far is below.
The Setup
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UpdateMe { get; set; }
public static List<Person> CreateList()
{
return new List<Person>
{
new Person { LastName = "Barton", FirstName = "Clint" },
new Person { LastName = "Stark", FirstName = "Tony" },
new Person { LastName = "Parker", FirstName = "Peter" }
};
}
public static List<Person> CreateListTwo()
{
return new List<Person>
{
new Person { LastName = "Barton", FirstName = "Clint", UpdateMe = "Updated"},
new Person { LastName = "Stark", FirstName = "Tony", UpdateMe = "Updated"},
};
}
}
public class FirstLastNameMatch : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.FirstName == y.FirstName
&& x.LastName == y.LastName;
}
public int GetHashCode(Person obj)
{
unchecked
{
var hash = 17;
hash = hash * 23 + obj.FirstName.GetHashCode();
hash = hash * 23 + obj.LastName.GetHashCode();
return hash;
}
}
}
Implementation
static void Main()
{
var listA = Person.CreateList();
var listB = Person.CreateListTwo();
//Attempt
var result = listA.Join(
listB,
x => x,
y => y,
(x, y) => new Person
{
FirstName = x.FirstName,
LastName = x.LastName,
UpdateMe = y.UpdateMe
},
new FirstLastNameMatch()
);
foreach (var person in result)
{
Console.WriteLine($"Name: {person.FirstName} {person.LastName} " +
$"UpdateMe: {person.UpdateMe} ");
}
}
There problem I run into here is that it is only an inner join and not a left join. I have found a way to do the left join but I cannot figure out how to pass in the IEqualityComparer into this syntax.
var result = from personA in listA
join personB in listB on new {personA.FirstName, personA.LastName } equals new { personB.FirstName, personB.LastName }
into buffer
from subPerson in buffer.DefaultIfEmpty()
select new Person
{
FirstName = personA.FirstName,
LastName = personA.LastName,
UpdateMe = (subPerson == null ? string.Empty : subPerson.UpdateMe)
};
The real catch is that once I am able to complete the left join while injecting the comparison criteria, I still need to detect duplicates.
I need to be able to identify if a person from listA matches to more than one person in listB. It is alright if listA had duplicates though.
End Goal
Left join 2 lists with dynamic matching criteria.
Detect multiple matches from the right list.
I need to update a static list of properties on the left list with the match from the right list.

How to copy all fields from one object to another, but only when destination value is NULL

I'd like to merge one object into another in a generic way that can be used repeatedly for other more complex objects. I only want the NULLs to change.
ie. merge sourcePerson and detinationPerson to get resultingPerson (below)
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int? Age { get; set; }
public string Address { get; set; }
}
var sourcePerson = new Person
{
FirstName = "Bill",
LastName = "Smith",
Age = 43,
Address = "123 Some Street"
};
var destinationPerson = new Person
{
FirstName = "Barbara",
LastName = null,
Age = 41,
Address = null
};
var resultingPerson = new Person
{
FirstName = "Barbara",
LastName = "Smith",
Age = 41,
Address = "123 Some Street"
};
I've tried Automapper, but can't seem to configure it properly. I feel like the following should work. Help?
Mapper.CreateMap<Person, Person>().ForAllMembers(opt => opt.UseDestinationValue());
Mapper.Map(sourcePerson, destinationPerson);
Thanks IronGeek and tutok.
Yes, the following works:
Mapper.CreateMap<Person, Person>()
.ForAllMembers(opt => opt.Condition(person => person.DestinationValue == null));
Your can use reflection like this:
public static T Merge<T>(T source, T destination)
{
var returnvalue = (T) Activator.CreateInstance(typeof (T));
foreach (var field in destination.GetType().GetProperties())
{
field.SetValue(returnvalue,
field.GetValue(destination, null) == null ? field.GetValue(source) : field.GetValue(destination));
}
return returnvalue;
}
I haven't tested this for other than simple DTOs.

Uppercase a List of object with LINQ

I have the code below. I'd like to convert all items in this list to uppercase.
Is there a way to do this in Linq ?
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public class MyClass
{
List<Person> myList = new List<Person>{
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
}
Update
I don't want to loop or go field by field. Is there a way by reflection to uppercase the value for each property?
Why would you like to use LINQ?
Use List<T>.ForEach:
myList.ForEach(z =>
{
z.FirstName = z.FirstName.ToUpper();
z.LastName = z.LastName.ToUpper();
});
EDIT: no idea why you want to do this by reflection (I wouldn't do this personally...), but here's some code that'll uppercase all properties that return a string. Do note that it's far from being perfect, but it's a base for you in case you really want to use reflection...:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public static class MyHelper
{
public static void UppercaseClassFields<T>(T theInstance)
{
if (theInstance == null)
{
throw new ArgumentNullException();
}
foreach (var property in theInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var theValue = property.GetValue(theInstance, null);
if (theValue is string)
{
property.SetValue(theInstance, ((string)theValue).ToUpper(), null);
}
}
}
public static void UppercaseClassFields<T>(IEnumerable<T> theInstance)
{
if (theInstance == null)
{
throw new ArgumentNullException();
}
foreach (var theItem in theInstance)
{
UppercaseClassFields(theItem);
}
}
}
public class Program
{
private static void Main(string[] args)
{
List<Person> myList = new List<Person>{
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
MyHelper.UppercaseClassFields<Person>(myList);
Console.ReadLine();
}
}
LINQ does not provide any facilities to update underlying data. Using LINQ, you can create a new list from an existing one:
// I would say this is overkill since creates a new object instances and
// does ToList()
var updatedItems = myList.Select(p => new Person
{
FirstName = p.FirstName.ToUpper(),
LastName = p.LastName.ToUpper(),
Age = p.Age
})
.ToList();
If using LINQ is not principal, I would suggest using a foreach loop.
UPDATE:
Why you need such solution? Only one way of doing this in generic manner - reflection.
the Easiest approach will be to use ConvertAll:
myList = myList.ConvertAll(d => d.ToUpper());
Not too much different than ForEach loops the original list whereas ConvertAll creates a new one which you need to reassign.
var people = new List<Person> {
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
people = people.ConvertAll(m => new Person
{
FirstName = m.FirstName?.ToUpper(),
LastName = m.LastName?.ToUpper(),
Age = m.Age
});
to answer your update
I don't want to loop or go field by field. Is there a way by
reflection to uppercase the value for each property?
if you don't want to loop or go field by field.
you could use property on the class to give you the Uppercase like so
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string FirstNameUpperCase => FirstName.ToUpper();
public string LastNameUpperCase => LastName.ToUpper();
}
or you could use back field like so
public class Person
{
private string _firstName;
public string FirstName {
get => _firstName.ToUpper();
set => _firstName = value;
}
private string _lastName;
public string LastName {
get => _lastName.ToUpper();
set => _lastName = value;
}
public int Age { get; set; }
}
You can only really use linq to provide a list of new objects
var upperList = myList.Select(p=> new Person {
FirstName = (p.FirstName == null) ? null : p.FirstName.ToUpper(),
LastName = (p.LastName == null) ? null : p.LastName.ToUpper(),
Age = p.Age
}).ToList();
p.lastname.ToString().ToUpper().Contains(TextString)

Categories

Resources