create querystring using lambda from List T - c#

i am having some difficulties creating a proper select.
i have my custom class:
internal class classA{
internal string FieldName { get; set; }
internal string FieldValue { get; set; }
internal bool IncludeInChecksum { get; set; }
}
what i am trying to do is to build and concatinate a querystring using the class list from above.
for the following object:
List<classA> requestParams = ...
the query string should look like:
a=1&b=2&c=3
ordered by FieldName ascending
where IncludeInChecksum = true
FieldName = FieldValue
preMD5= requestParams
.Where(x=>x.IncludeInChecksum)
.OrderBy(y=>y.FieldName)
.Aggregate((i,j)=>(i.FieldName+ "=" +j.FieldValue));
this is where i am stuck.
thanks in advance

I will try to get all name=value strings with the help of the Select() method. And then convert the result to array. After that just use String.Join() method to get the desired result.
Join(String, String[]) concatenates all the elements of a string array, using the specified separator between each element.
var preMD5= requestParams
.Where(x => x.IncludeInChecksum)
.OrderBy(y => y.FieldName)
.Select(z => string.Format("{0}={1}", z.FieldName, z.FieldValue))
.ToArray();
preMD5 = string.Join("&", preMD5);

Aggregate aggregates values from different rows. You need to combine values from different fields. For this you use Select:
requestParms.Where(...).OrderBy(...).Select(f=>f.FieldName+"="+f.FieldValue)
This will return an IEnumerable of name=value strings. You can use string.Join to combine them into one string.

I know it's been answered,
but still I wanted to provide my solution.
I used a dedicated method to build the query string parameter,
and an extension method to concat it all.
Hope this helps.
public class classA
{
internal string FieldName { get; set; }
internal string FieldValue { get; set; }
internal bool IncludeInChecksum { get; set; }
public classA(string fieldName, string fieldValue, bool includeInChecksum)
{
this.FieldName = fieldName;
this.FieldValue = fieldValue;
this.IncludeInChecksum = includeInChecksum;
}
public string ToQueryString()
{
return string.Format("{0}={1}",
this.FieldName,
this.FieldValue);
}
public void Test()
{
var list = new List<classA> {
new classA("A", "1", true) ,
new classA("D", "4", true) ,
new classA("B", "2", false) ,
new classA("C", "3", true)
};
var result = list.
Where(o => o.IncludeInChecksum).
OrderBy(o => o.FieldName).
Select(o => o.ToQueryString()).
ToStringEx("&");
}
}
public static class ExtensionMethods
{
public static string ToStringEx<T>(this IEnumerable<T> items, string separetor = ",")
{
if (items == null)
{
return "null";
}
return string.Join(separetor, items.Select(o => o != null ? o.ToString() : "[null]").ToArray());
}
}

Related

How to turn an object into a request in C#

My Model, example:
public class Obj
{
public Obj()
{ }
public int? IdRestricao { get; set; }
public int? IdTipoRestringido { get; set; }
public string CodRestringido { get; set; }
public string NomeRestringido { get; set; }
public int? IdTipoRestricao { get; set; }
public string CodRestricao { get; set; }
public string NomeRestricao { get; set; }
public DateTime? PeriodoInicio { get; set; }
public DateTime? PeriodoFim { get; set; }
public int? IdStatus { get; set; }
}
Request
www.url.com.br?IdRestricao=1&IdTipoRestringido=2&CodRestringido=3&NomeRestringido=4&IdTipoRestricao=5&CodRestricao=6&NomeRestricao=7&PeriodoInicio=8&PeriodoFim=9
code:
var model = obj.*tostring()*//
something like
var request = new RestRequest($"/api/{**model**}", Method.GET);
edit1:
I'm wondering if there is a library or something that transforms my object in my request as in the examples above
You can override ToString to format the data from the Obj class into GET parameters.
You could write a utility function to convert all object properties into nameof and the associated value into key-value pairs.
Something such as this answer to convert your Obj into a dictionary, and then another method to convert the dictionary into Prop=value could be done.
For a one off something like this would be suitable:
public override string ToString()
{
return $"IdRestricao={IdRestricao}&IdTipoRestringido={IdTipoRestringido}&CodRestringido={CodRestringido}...
}
I found the answer to what I was looking for, it follows below in case anyone needs it in the future
private static string ToQueryString(this object request, string separator = ",")
{
if (request == null)
throw new ArgumentNullException("request");
// Get all properties on the object
var properties = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(request, null));
// Get names for all IEnumerable properties (excl. string)
var propertyNames = properties
.Where(x => !(x.Value is string) && x.Value is IEnumerable)
.Select(x => x.Key)
.ToList();
// Concat all IEnumerable properties into a comma separated string
foreach (var key in propertyNames)
{
var valueType = properties[key].GetType();
var valueElemType = valueType.IsGenericType
? valueType.GetGenericArguments()[0]
: valueType.GetElementType();
if (valueElemType.IsPrimitive || valueElemType == typeof(string))
{
var enumerable = properties[key] as IEnumerable;
properties[key] = string.Join(separator, enumerable.Cast<object>());
}
}
// Concat all key/value pairs into a string separated by ampersand
return string.Join("&", properties
.Select(x => string.Concat(
Uri.EscapeDataString(x.Key), "=",
Uri.EscapeDataString(x.Value.ToString()))));
}

How to fill properties of objects using LINQ and return collection

I have file with lines like this: 05122018;surname1;ItemName1;Price1
And OrderModel
public class OrderModel
{
public string ManagerSurname { get; set; }
public DateTime Date { get; set; }
public string CustomerSurname { get; set; }
public string ItemName { get; set; }
public double Price { get; set; }
}
How to make LINQ request to return collection of OrderModels ?
public IEnumerable<OrderModel> Parse(string[] input)
{
return
(input ?? throw new ArgumentNullException(nameof(input)))
.Select(orderModel => new OrderModel()
{
//filling objects
}).Where(orderModel => //Any);
Considering the input[] array consists of one line
05122018;surname1;ItemName1;Price1
for each entry and each line has the same order of items. You could do a split in the select, to get the appropriate values:
public IEnumerable<OrderModel> Parse(string[] input)
{
return
(input ?? throw new ArgumentNullException(nameof(input)))
.Select(orderModel =>
{
var items = orderModel.Split(";");
if (items.Length != 4)
{
throw new ArgumentException();
}
return new OrderModel()
{
//filling objects
ManagerSurname = items[1],
ItemName = items[2],
...
};
}).Where(orderModel => //Any);
}
Basically, you look like you want to deserialize the string that's coming in from semicolon delimited to an object. That's a pretty ugly and potentially fragile way to serialize / deserialize your data, and if you can change it (to JSON, or XML or something else) it might be a good idea.
But you could create a constructor for your OrderModel class that takes the serialized string as a parameter and deserializes in there:
public OrderModel(string inOrderModel){
var splitString = inOrderModel.Split(';');
ManagerSurname = splitString[1];
// etc
}
And then you can use LINQ to create the list from the incoming array:
return input.Select(i => new OrderModel(i));
returning simply return _context.OrderModel will return your entire OrderModel from the context:
public IEnumerable<OrderModel> Parse(string[] input)
{
(input ?? throw new ArgumentNullException(nameof(input)))
return _context.OrderModel;
}
or assuming you would like to filter your dataset by data in the input string array, you can filter with input.contains(model.field). Example filters Item Name based on the input string array:
public IEnumerable<OrderModel> Parse(string[] input)
{
(input ?? throw new ArgumentNullException(nameof(input)))
return _context.OrderModel.Where(m => input.contains(m.ItemName));
}

parsing string for dropdownlist options

I have a string (q.ADDLOption) with values like
Select,|IE,IE|Safari,Safari|Chrome,Chrome|
I want to parse it to be the options in a dropdownlist
Optionddl oddl = q.ADDLOption.Split('|').ToList<Optionddl>(); <== this is giving error
I have also a class
public class Optionddl
{
public string text { get; set; }
public string value { get; set; }
}
This might do the trick for you
List<Optionddl> oddl = q.ADDLOption.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => new Optionddl
{
text = x.Split(',')[0],
value = x.Split(',')[1]
})
.ToList<Optionddl>();
The first problem with the code is q.ADDLOption.Split.ToList is going to return a list and not an object of Optionddl. Secondly we cannot directly convert an array of string[] to List because 'string[]' does not contain a definition for 'ToList' and the best extension method overload 'System.Linq.Enumerable.ToList(System.Collections.Generic.IEnumerable)' has some invalid arguments would be the error. Lastly it is optional to create ToList or ToList<Optionddl>.
Hope this helps
Because Optionddl is not something can be converted to a List.
Consider this:
List<Optionddl> oddl = q.ADDLOption.Split(new string[]{'|'}).ToList<Optionddl>();
Alternatively you can create some implicit/explicit operators :
public class Optionddl
{
public string text { get; set; }
public string value { get; set; }
public static implicit operator string(Optionddl option)
{
return option.text + "," + option.value;
}
public static implicit operator Optionddl(string str)
{
string[] extracted = str.Split(",");
return new Optionddl { text = extracted[0], value = extracted[1] };
}
}
This way you can make something like :
Optionddl meOption = new Optionddl { value = "IE", text = "IE" };
string meOptionString = meOption; // result of meOptionString = "IE,IE"
meOption = meOptionString; // result of meOption = { text = "IE", value = "IE" }

Convert Array to custom object list c#

I have a array:-
private string[][] barValues = new string[][] { new string[]{ "1.9", "5.8", "4.8", "Since Inception", "24-Jan 2014 to 24 Jun 2014" },
new string[]{"1.2", "16.5","9.8", "Year to date","01-Apr 2014 to 24-Jun 2014" },
new string[]{"11.6","28.8","23.5","Last quarter","01-Jan to 24-Jun 2014"} };
I want to convert this array into my custom list :-
List<Portfolio> list = new List<Portfolio>();
I tried doing :-
List<Portfolio> list=myArray.Cast<Portfolio>().ToList();
But I get a error:-
System.InvalidCastException: Cannot cast from source type to
destination type.
How do I do this conversion?
You will need to use the Select operator and assign your array of strings to your Portfolio object. Something like this:
myArray.Select(array => new Portfolio { Field1 = array[0], Field2 = array[1] }).ToList()
There is no "magic" conversion from string[] to your class PortFolio, you have to do it manually.
This could work:
List<Portfolio> portFolioList = barValues
.Select(sArr => new Portfolio
{
Values = sArr.Take(3).Select(double.Parse).ToList(),
Name = sArr.Skip(3).First(),
TimeSpan = sArr.Last()
}).ToList();
If you have a class like this:
public class Portfolio
{
public List<double> Values { get; set; }
public string Name { get; set; }
public string TimeSpan { get; set; }
}
Convert Array to string try this way
string[] arr = ...
List<object> list= new List<object>(arr);
foreach(object obj in arr)
list.add(obj);
var converted = barValues.Select(a => new {Value1 = a[0], Value2= a[1]}).ToArray();
This gives you an array of anonymous objects. Just replace my anonymous constructor with your constructor of Portfolio. Lambda variable a is the array that contains the string values.
Create a constructor taking the items from barValues and iterator over those.
like:
class PortFolio()
{
PortFolio(string e1, string e2, string e3, string period, string date)
{
// Copy to properties.
}
...
}
Then in the code for copying:
foreach (var barValue in barValues)
{
list.Add(new PortFolio(barValue[0], barValue[1], barValue[2], barValue[3], barValue[4]); }
}
C# is a strongly typed language. There is no out of the box way to magically cast a bunch of string into an object.
A correct way to do what you want is to first add a constructor on your Portfolio class that takes an array of strings and assign its values to your instance properties:
class Portfolio
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public string Method { get; set; }
public string Period { get; set; }
public Portfolio(string[] values)
{
if (values != null)
{
this.Value1 = values.ElementAtOrDefault(0);
this.Value2 = values.ElementAtOrDefault(1);
this.Value3 = values.ElementAtOrDefault(2);
this.Method = values.ElementAtOrDefault(3);
this.Period = values.ElementAtOrDefault(4);
}
}
}
You can then use linq to build your list :
var portfoliosList = barValues.Select(values => new Portfolio(values)).ToList();
If required, you can do additional work in the constructor, for instance converting the values to decimal, the method to an Enum and the period as a couple of DateTime.
If you cannot modify the Portfolio class, because it's a third party component or out of your scope, you can create a factory class with a method containing that logic :
static class ProtfolioFactory
{
static public Portfolio BuildPortfolio(string[] values)
{
var portfolio = new Portfolio();
if (values != null)
{
portfolio.Value1 = values.ElementAtOrDefault(0);
portfolio.Value2 = values.ElementAtOrDefault(1);
portfolio.Value3 = values.ElementAtOrDefault(2);
portfolio.Method = values.ElementAtOrDefault(3);
portfolio.Period = values.ElementAtOrDefault(4);
}
return portfolio;
}
}
The linq query then becomes :
var list = barValues.Select(values => ProtfolioFactory.BuildPortfolio(values)).ToList();

How to copy a List<> to another List<> with Comparsion in c#

I an having Two Lists. I want to get the matched and unmatched values based on ID and add the results to another List. I can get both of these using Intersect/Except.
But I can get only ID in the resultant variables (matches and unmatches) . I need all the properties in the Template.
List<Template> listForTemplate = new List<Template>();
List<Template1> listForTemplate1 = new List<Template1>();
var matches = listForTemplate .Select(f => f.ID)
.Intersect(listForTemplate1 .Select(b => b.ID));
var ummatches = listForTemplate .Select(f => f.ID)
.Except(listForTemplate1.Select(b => b.ID));
public class Template
{
public string ID{ get; set; }
public string Name{ get; set; }
public string Age{ get; set; }
public string Place{ get; set; }
public string City{ get; set; }
public string State{ get; set; }
public string Country{ get; set; }
}
public class Template1
{
public string ID{ get; set; }
}
If you don't want to implement IEquality for this simple task, you can just modify your LINQ queries:
var matches = listForTemplate.Where(f => listForTemplate1.Any(b => b.ID == f.ID));
and
var unmatches = listForTemplate.Where(f => listForTemplate1.All(b => b.ID != f.ID));
You might want to check for null before accessing ID, but it should work.
You are looking for the overloaded function, with the second parameter IEqualityComparer. So make your comparer ( example: http://www.blackwasp.co.uk/IEqualityComparer.aspx ), and use the same comparer in intersect / except.
And for the generic part: maybe you should have a common interface for templates e.g. ObjectWithID describing that the class have a string ID property. Or simply use dynamic in your comparer (but I think this is very-very antipattern because you can have run time errors if using for the bad type).
You also have a problem: intersecting two collections with two different types will result in a collection of Object (common parent class). Then you have to cast a lot (antipattern). I advise you to make a common abstract class/interface for your template classes, and it is working. If you need to cast the elements back, do not cast, but use the visitior pattern: http://en.wikipedia.org/wiki/Visitor_pattern
Example (good):
static void Main(string[] args)
{
// http://stackoverflow.com/questions/16496998/how-to-copy-a-list-to-another-list-with-comparsion-in-c-sharp
List<Template> listForTemplate = new Template[] {
new Template(){ID = "1"},
new Template(){ID = "2"},
new Template(){ID = "3"},
new Template(){ID = "4"},
new Template(){ID = "5"},
new Template(){ID = "6"},
}.ToList();
List<Template1> listForTemplate1 = new Template1[] {
new Template1(){ID = "1"},
new Template1(){ID = "3"},
new Template1(){ID = "5"}
}.ToList();
var comp = new ObjectWithIDComparer();
var matches = listForTemplate.Intersect(listForTemplate1, comp);
var ummatches = listForTemplate.Except(listForTemplate1, comp);
Console.WriteLine("Matches:");
foreach (var item in matches) // note that item is instance of ObjectWithID
{
Console.WriteLine("{0}", item.ID);
}
Console.WriteLine();
Console.WriteLine("Ummatches:");
foreach (var item in ummatches) // note that item is instance of ObjectWithID
{
Console.WriteLine("{0}", item.ID);
}
Console.WriteLine();
}
}
public class ObjectWithIDComparer : IEqualityComparer<ObjectWithID>
{
public bool Equals(ObjectWithID x, ObjectWithID y)
{
return x.ID == y.ID;
}
public int GetHashCode(ObjectWithID obj)
{
return obj.ID.GetHashCode();
}
}
public interface ObjectWithID {
string ID { get; set; }
}
public class Template : ObjectWithID
{
public string ID { get; set; }
public string Name { get; set; }
public string Age { get; set; }
public string Place { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Template1 : ObjectWithID
{
public string ID { get; set; }
}
Output:
Matches:
1
3
5
Ummatches:
2
4
6
Press any key to continue . . .
For comparison, this should also work (the first part is a variation on #MAV's answer):
var matches = from item in listForTemplate
join id in listForTemplate1 on item.ID equals id.ID
select item;
var unmatches = listForTemplate.Where(item => matches.All(elem => elem.ID != item.ID));
matches and unmatches will both be IEnumerable<Template> which is the type you require.
However, MAV's answer works fine so I'd go for that one.
As mentioned, Implement the IEqualityComparer<T> interface.
IEqualityComparer<T> MSDN
Then use this as an argument in your method for Except() and Intersect()
Intersect
There is a good example of how to do so on the link for the Intersect() method.
If you don't absolutely have to use LINQ, why not code something like this?
var matches = new List<Template>();
var unmatches = new List<Template>();
foreach (var entry in listForTemplate)
{
bool matched = false;
foreach (var t1Entry in listForTemplate1)
{
if (entry.ID == t1Entry.ID)
{
matches.Add(entry);
matched = true;
break;
}
}
if (!matched)
{
unmatches.Add(entry);
}
}
A disadvantage of the LINQ approach is that you're traversing the lists twice.

Categories

Resources