read CSV file Save output to LIST using LINQ - linq

I have a sample CSV file as follows
1,A
2,B
3,C
Code:
var query = File.ReadAllLines("test.txt")
.Select(record => record.Split(','))
.Select(tokens => new { clearNum = tokens[0], MPID = tokens[1] });
foreach (var item in query)
{
Console.WriteLine("{0}, {1}", item.clearNum, item.MPID);
}
I am able to print the items.
I need to send the output of LINQ query to LIST
public class icSASList
{
public string ClearNum { get; set; }
public string MPID { get; set; }
}
List clearList = new List;

After considering the accepted answer, I'd like to suggest a solution that requires less object initializations. If the list is large, this will make a difference.
var query = File.ReadAllLines("test.txt")
.Select(record => record.Split(','))
.Select(tokens => new icSASList(){ ClearNum = tokens[0], MPID = tokens[1] });
var clearList = query.ToList();
Oh, yeah, using record.Split(',') is naive - it's normally allowed to have commas in " (quoted) fields, which will break your program. Better use something like http://www.filehelpers.com/.

I've not tried compiling it but I think you want something like this?
var clearList = query.Select(x=>new icSASList(){ClearNum = x.clearNum, MPID = x.MPID}).ToList();

Related

Filtering out values from a list of object in a List

I have an IEnumerable collection of UnitGroup: IEnumerable<UnitGroup>,
class UnitGroup
{
string key { get; set; }
List<UnitType> NameList { get; set; }
}
class UnitType
{
String UnitName{ get; set; }
Description { get; set; }
}
Now I want to filterIEnumerable<UnitGroup> based on UnitType's UnitName.
For example I want to get only the records of UnitName that contains a string and remove remaining.
something like this:
IEnumerable<UnitGroup> Groups;
IEnumerable<UnitGroup> filteredResult = Groups.NameList(o => o.UnitName.contains("test"));
And get IEnumerable<UnitGroup> with only filtered UnitNames under UnitType under UnitGroup.
What is the best way of acheiving this?
I'm not 100% sure what you're trying to achieve. Could you provide some sample data, to make it more clear?
Although, I think it may fit into your goal:
IEnumerable<UnitGroup> Groups;
var filteredResult = Groups.Select(g => new UnitGroup {
key = g.key,
NameList = g.NameList
.Where(n => n.UnitName == "test")
.ToList()
})
.Where(g => g.NameList.Count > 0);
Here is another way that should do what #MarcinJuraszek answers does. (I am guessing the intent of the question as well.)
IEnumerable<UnitGroup> Groups;
var filteredResult = Groups.Where (g => g.NameList.Count() > g.NameList.RemoveAll(nl => nl.UnitName != "Name1"));
If the number of removed items was less than the original count, then we have items that are of interest, so select the parent.
Note: This will modify the original collection, so if you need to filter it more than once then this is not the answer you are looking for.
Try this:
var filteredList = from g in Groups
where g.NameList.Exists(i=>i.UnitName=="test")
select g;

Find / Count Redundant Records in a List<T>

I am looking for a way to identify duplicate records...only I want / expect to see them.
So the records aren't duplicated completely but the unique fields I am unconcerned with at this point. I just want to see if they have made X# payments of the exact same amount, via the exact same card, to the exact same person. (Bogus example just to illustrate)
The collection is a List<> further whatever X# is the List<>.Count will be X#. In other words all the records in the list match (again just the fields I am concerned with) or I will reject it.
The best I can come up with is to take the first record get value of say PayAmount and LINQ the other two to see if they have the same PayAmount value. Repeat for all fields to be matched. This seems horribly inefficient but I am not smart enough to think of a better way.
So any thoughts, ideas, pointers would be greatly appreciated.
JB
Something like this should do it.
var duplicates = list.GroupBy(x => new { x.Amount, x.CardNumber, x.PersonName })
.Where(x => x.Count() > 1);
Working example:
class Program
{
static void Main(string[] args)
{
List<Entry> table = new List<Entry>();
var dup1 = new Entry
{
Name = "David",
CardNumber = 123456789,
PaymentAmount = 70.00M
};
var dup2 = new Entry
{
Name = "Daniel",
CardNumber = 987654321,
PaymentAmount = 45.00M
};
//3 duplicates
table.Add(dup1);
table.Add(dup1);
table.Add(dup1);
//2 duplicates
table.Add(dup2);
table.Add(dup2);
//Find duplicates query
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
foreach (var item in query)
{
Console.WriteLine("{0}, {1}, {2}, {3}", item.name, item.cardNumber, item.amount, item.count);
}
Console.ReadKey();
}
}
public class Entry
{
public string Name { get; set; }
public int CardNumber { get; set; }
public decimal PaymentAmount { get; set; }
}
The meat of which is this:
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
You're unique entries are based off of the 3 criteria of Name, Card Number, and Payment Amount so you group by them and then use .Count() to count how many of those unique values exist. where g.Count() > 1 filters the group to duplicates only.

string replace using Linq in c#

public class Abbreviation
{
public string ShortName { get; set; }
public string LongName { get; set; }
}
I have a list of Abbreviation objects like this:
List abbreviations = new List();
abbreviations.add(new Abbreviation() {ShortName = "exp.", LongName = "expression"});
abbreviations.add(new Abbreviation() {ShortName = "para.", LongName = "paragraph"});
abbreviations.add(new Abbreviation() {ShortName = "ans.", LongName = "answer"});
string test = "this is a test exp. in a para. contains ans. for a question";
string result = test.Replace("exp.", "expression")
...
I expect the result to be:
"this is a test expression in a paragraph contains answer for a question"
Currently I am doing:
foreach (Abbreviation abbreviation in abbreviations)
{
test = test.Replace(abbreviation.ShortName, abbreviation.LongName);
}
result = test;
Wondering if there is a better way using a combination of Linq and Regex.
If you really wanted to shorten your code, you could use the ForEach extension method on the List:
abbreviations.ForEach(x=> test=test.Replace(x.ShortName, x.LongName));
You could use the ForEach method. Also, a StringBuilder should make the operations on your string more efficient:
var test = new StringBuilder("this is a test exp. in a para. contains ans. for a question");
abbreviations.ForEach(abb => test = test.Replace(abb.ShortName, abb.LongName));
return test.ToString();
Try this one
TestList.ForEach(x => x.TestType.Replace("", "DataNotAvailable"));
or the one below
foreach (TestModel item in TestList.Where(x => x.ID == ""))
{
item.ID = "NoDataAvailable";
}

Dynamically Sorting with LINQ

I have a collection of CLR objects. The class definition for the object has three properties: FirstName, LastName, BirthDate.
I have a string that reflects the name of the property the collection should be sorted by. In addition, I have a sorting direction. How do I dynamically apply this sorting information to my collection? Please note that sorting could be multi-layer, so for instance I could sort by LastName, and then by FirstName.
Currently, I'm trying the following without any luck:
var results = myCollection.OrderBy(sortProperty);
However, I'm getting a message that says:
... does not contain a defintion for 'OrderBy' and the best extension method overload ... has some invalid arguments.
Okay, my argument with SLaks in his comments has compelled me to come up with an answer :)
I'm assuming that you only need to support LINQ to Objects. Here's some code which needs significant amounts of validation adding, but does work:
// We want the overload which doesn't take an EqualityComparer.
private static MethodInfo OrderByMethod = typeof(Enumerable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(method => method.Name == "OrderBy"
&& method.GetParameters().Length == 2)
.Single();
public static IOrderedEnumerable<TSource> OrderByProperty<TSource>(
this IEnumerable<TSource> source,
string propertyName)
{
// TODO: Lots of validation :)
PropertyInfo property = typeof(TSource).GetProperty(propertyName);
MethodInfo getter = property.GetGetMethod();
Type propType = property.PropertyType;
Type funcType = typeof(Func<,>).MakeGenericType(typeof(TSource), propType);
Delegate func = Delegate.CreateDelegate(funcType, getter);
MethodInfo constructedMethod = OrderByMethod.MakeGenericMethod(
typeof(TSource), propType);
return (IOrderedEnumerable<TSource>) constructedMethod.Invoke(null,
new object[] { source, func });
}
Test code:
string[] foo = new string[] { "Jon", "Holly", "Tom", "William", "Robin" };
foreach (string x in foo.OrderByProperty("Length"))
{
Console.WriteLine(x);
}
Output:
Jon
Tom
Holly
Robin
William
It even returns an IOrderedEnumerable<TSource> so you can chain ThenBy clauses on as normal :)
You need to build an Expression Tree and pass it to OrderBy.
It would look something like this:
var param = Expression.Parameter(typeof(MyClass));
var expression = Expression.Lambda<Func<MyClass, PropertyType>>(
Expression.Property(param, sortProperty),
param
);
Alternatively, you can use Dynamic LINQ, which will allow your code to work as-is.
protected void sort_grd(object sender, GridViewSortEventArgs e)
{
if (Convert.ToBoolean(ViewState["order"]) == true)
{
ViewState["order"] = false;
}
else
{
ViewState["order"] = true;
}
ViewState["SortExp"] = e.SortExpression;
dataBind(Convert.ToBoolean(ViewState["order"]), e.SortExpression);
}
public void dataBind(bool ord, string SortExp)
{
var db = new DataClasses1DataContext(); //linq to sql class
var Name = from Ban in db.tbl_Names.AsEnumerable()
select new
{
First_Name = Ban.Banner_Name,
Last_Name = Ban.Banner_Project
};
if (ord)
{
Name = BannerName.OrderBy(q => q.GetType().GetProperty(SortExp).GetValue(q, null));
}
else
{
Name = BannerName.OrderByDescending(q => q.GetType().GetProperty(SortExp).GetValue(q, null));
}
grdSelectColumn.DataSource = Name ;
grdSelectColumn.DataBind();
}
you can do this with Linq
var results = from c in myCollection
orderby c.SortProperty
select c;
For dynamic sorting you could evaluate the string i.e. something like
List<MyObject> foo = new List<MyObject>();
string sortProperty = "LastName";
var result = foo.OrderBy(x =>
{
if (sortProperty == "LastName")
return x.LastName;
else
return x.FirstName;
});
For a more generic solution see this SO thread: Strongly typed dynamic Linq sorting
For this sort of dynamic work I've been using the Dynamic LINQ library which makes this sort of thing easy:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
http://msdn2.microsoft.com/en-us/vcsharp/bb894665.aspx
You can copy paste the method I post in that answer, and change the signature/method names:
How to make the position of a LINQ Query SELECT variable
You can actually use your original line of code
var results = myCollection.OrderBy(sortProperty);
simply by using the System.Linq.Dynamic library.
If you get a compiler error (something like cannot convert from or does not contain a definition...) you may have to do it like this:
var results = myCollection.AsQueryable().OrderBy(sortProperty);
No need for any expression trees or data binding.
You will need to use reflection to get the PropertyInfo, and then use that to build an expression tree. Something like this:
var entityType = typeof(TEntity);
var prop = entityType.GetProperty(sortProperty);
var param = Expression.Parameter(entityType, "x");
var access = Expression.Lambda(Expression.MakeMemberAccess(param, prop), param);
var ordered = (IOrderedQueryable<TEntity>) Queryable.OrderBy(
myCollection,
(dynamic) access);

GroupBy String and Count in LINQ

I have got a collection. The coll has strings:
Location="Theater=1, Name=regal, Area=Area1"
Location="Theater=34, Name=Karm, Area=Area4445"
and so on. I have to extract just the Name bit from the string. For example, here I have to extract the text 'regal' and group the query. Then display the result as
Name=regal Count 33
Name=Karm Count 22
I am struggling with the query:
Collection.Location.GroupBy(????);(what to add here)
Which is the most short and precise way to do it?
Yet another Linq + Regex approach:
string[] Location = {
"Theater=2, Name=regal, Area=Area1",
"Theater=2, Name=regal, Area=Area1",
"Theater=34, Name=Karm, Area=Area4445"
};
var test = Location.Select(
x => Regex.Match(x, "^.*Name=(.*),.*$")
.Groups[1].Value)
.GroupBy(x => x)
.Select(x=> new {Name = x.Key, Count = x.Count()});
Query result for tested strings
Once you've extracted the string, just group by it and count the results:
var query = from location in locations
let name = ExtractNameFromLocation(location)
group 1 by name in grouped
select new { Name=grouped.Key, Count=grouped.Count() };
That's not particularly efficient, however. It has to do all the grouping before it does any counting. Have a look at this VJ article for an extension method for LINQ to Objects,
and this one about Push LINQ which a somewhat different way of looking at LINQ.
EDIT: ExtractNameFromLocation would be the code taken from answers to your other question, e.g.
public static string ExtractNameFromLocation(string location)
{
var name = (from part in location.Split(',')
let pair = part.Split('=')
where pair[0].Trim() == "Name"
select pair[1].Trim()).FirstOrDefault();
return name;
}
Here is another LINQ alternative solution with a working example.
static void Main(string[] args)
{
System.Collections.Generic.List<string> l = new List<string>();
l.Add("Theater=1, Name=regal, Area=Area"); l.Add("Theater=34, Name=Karm, Area=Area4445");
foreach (IGrouping<string, string> g in l.GroupBy(r => extractName(r)))
{
Console.WriteLine( string.Format("Name= {0} Count {1}", g.Key, g.Count()) );
}
}
private static string extractName(string dirty)
{
System.Text.RegularExpressions.Match m =
System.Text.RegularExpressions.Regex.Match(
dirty, #"(?<=Name=)[a-zA-Z0-9_ ]+(?=,)");
return m.Success ? m.Value : "";
}

Resources