Sort List<> by when item is added? - c#

I currently have a list of items from an object. I send the list to another method where it is copied to a csv file. The problem that I have is that the items are being listed on the csv in alphabetical order instead of the order they were added. I there a way to keep the original order?
SampleDatalist.Add(new SampleDataViewModel
{
PlanID = Convert.ToString(allFields["AdditionalInfo-PlanDetails-planNumber"]),
SSN = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-ssn"]),
EmployeeNumber = "",
DivisionId = "",
FirstName = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-firstName"]),
MiddleName = "",
LastName = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-lastName"]),
StreetAddress = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-homeAddress1"]),
City = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-homeCity"]),
State = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-homeState"]),
PostalCode = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-homeZip"]),
HomePhone = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-homePhone"]),
OfficePhone = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-businessPhone"]),
Email = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-email"]),
DateOfBirth = Convert.ToString(allFields["AccountOwnerInfo-ContactInfo-dob"]),
DateOfHire = Convert.ToString(allFields["AdditionalInfo-PlanDetails-dateOfHire"]),
MaritalStatus = Convert.ToString(allFields["AdditionalInfo-PlanDetails-maritalStatus"])
});
WriteCSV(PCSDatalist, #"C:\Users\brand\Documents\New_PPT_" + dt + ".csv");
public void WriteCSV<T>(IEnumerable<T> items, string path)
{
Type itemType = typeof(T);
var props = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p => p.Name);
using (var writer = new StreamWriter(path, true))
{
foreach (var item in items)
{
writer.WriteLine(string.Join(", ", props.Select(p => p.GetValue(item, null))));
}
}
}

From the code you show, the items in the list show be written in the order they were added.
However, the properties of each item will be written in alphabetical order -- because you specifically ask that they are : .OrderBy(p => p.Name). If you do not what this, remove that clause, and the properties will be written in the order they are defined in the class.

Related

Using Linq to select multiple items per iteration?

Per iteration, this query creates an EmailRecipient for each populated address. Can it be done without the multiple iteration?
var addedRecipients = (from oldRecip in oldEmailRecipients
where !string.IsNullOrWhiteSpace(oldRecip.EmailAddress1)
select new EmailRecipient
{
UserName = oldRecip.UserName,
EmailAddress = oldRecip.EmailAddress1
}
).Union(from oldRecip in oldEmailRecipients
where !string.IsNullOrWhiteSpace(oldRecip.EmailAddress2)
select new EmailRecipient
{
UserName = oldRecip.UserName,
EmailAddress = oldRecip.EmailAddress2
});
You can use SelectMany extension method:
var addedRecipients = oldEmailRecipients.SelectMany(e=>
{
var result= new List<EmailRecipient>();
if(!string.IsNullOrWhiteSpace(e.EmailAddress1))
{
result.Add(new EmailRecipient
{
UserName = e.UserName,
EmailAddress = e.EmailAddress1
});
}
if(!string.IsNullOrWhiteSpace(e.EmailAddress2))
{
result.Add(new EmailRecipient
{
UserName = e.UserName,
EmailAddress = e.EmailAddress2
});
}
return result;
});
Update
The solution that I show above only works in Linq to Objects. Your comment suggest me you are using EF. A simple solution could be call AsEnumerable method before SelectMany to make the switch to Linq to Objects, but that could end harming your performance if you are not filtering your recipients first.
Another solution could be selecting only data that you need first from you server before of call SelectMany to not load other columns you don't need in this case:
...Where(...)
.Select(r=>new{UserName=r.UserName,
EmailAddress1=r.EmailAddress1,
EmailAddress2=r.EmailAddress2 })
.AsEnumerable()
.SelectMany(...);
Sticking with query syntax, and making sure to only process oldEmailRecipients items who have either a non-null/whitespace EmailAddress1 or a non-null/whitespace EmailAddress2:
var addedRecipients =
from oldEmail in oldEmailRecipients
let hasEmail1 = !string.IsNullOrWhiteSpace(oldEmail.EmailAddress1)
let hasEmail2 = !string.IsNullOrWhiteSpace(oldEmail.EmailAddress2)
where hasEmail1 || hasEmail2
let emailUserNameCombos = hasEmail1 && hasEmail2
? new[]
{
new {Email = oldEmail.EmailAddress1, oldEmail.UserName},
new {Email = oldEmail.EmailAddress2, oldEmail.UserName}
}
: hasEmail1
? new[] {new {Email = oldEmail.EmailAddress1, oldEmail.UserName}}
: new[] {new {Email = oldEmail.EmailAddress2, oldEmail.UserName}}
from emailUsername in emailUserNameCombos
select new EmailRecipient
{
UserName = emailUsername.UserName,
EmailAddress = emailUsername.Email
};
You can build an inline array to add both emails and flatten them out using SelectMany.
var addedRecipients = from oldRecip in oldEmailRecipients
let emails =
new[] {oldRecip.EmailAddress1, oldRecip.EmailAddress2}.Where(e => !string.IsNullOrWhiteSpace(e))
from email in emails
where emails.Any()
select new EmailRecipient
{
UserName = oldRecip.UserName,
EmailAddress = email
};
When your EmailRecipient has more than two email address then you could do this:
// Just building a pseudo dataclass
List<Recipient> oldEmailRecipients = Enumerable.Range(1, 10).Select(item => new Recipient()
{
Name = "Recipient" + item,
EmailAddress1 = "pseudo" + item + "#gmail.com",
EmailAddress2 = "pseudo" + (item + 1) + "#gmail.com",
//EmailAddress3 = "pseudo" + (item + 2) + "#gmail.com",
EmailAddress3 = "",
EmailAddress4 = "pseudo" + (item + 3) + "#gmail.com",
} ).ToList( )
// create anonymous object for each recipient and a list of valid adresses
var query = from mailRecipients in oldEmailRecipients
select new
{
Name = mailRecipients.Name,
Addresses = new List<string>()
{
mailRecipients.EmailAddress1,
mailRecipients.EmailAddress2,
mailRecipients.EmailAddress3,
mailRecipients.EmailAddress4
}.Where(item => string.IsNullOrEmpty( item ) == false )
};
// create an EmailRecipient for each valid combination
var final = from item in query
from address in item.Addresses
select new EmailRecipient
{
Name = item.Name,
Address = address
};

Nested group by based on flatten record set

I have the following responses from the API. How can I group them into the following structure?
Student[]
- Name
- Classes[]
- ClassName
- ClassId
- ClassCategories[]
- CategoryName
- CategoryWeight
- Assignments[]
- AssignmentName
- Score
I was managed to group them until the "Classes" level but unable to get the ClassCategories for each of the classes
var data = (from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial }
into StudentGroup
select new GroupedStudent
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = from result in results
group result by new { result.ClassId, result.ClassName } into ClassGroup
select new groupedClass
{
ClassName = ClassGroup.Key.ClassName,
ClassId = ClassGroup.Key.ClassId,
ClassCategories = ...
})
}).ToList();
Can anyone please assists me? Thank you.
First, you have make ClassGroup from StudentGroup not from results.
Classes = from s in StudentGroup group result by new { s.ClassId, s.ClassName } into ClassGroup
The complete linq query is as follows:
var data =
(from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial } into StudentGroup
select new
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = (from s in StudentGroup
group s by new { s.ClassId, s.ClassName } into ClassGroup
select new
{
ClassId = ClassGroup.Key.ClassId,
ClassName = ClassGroup.Key.ClassName,
ClassCategories = (from c in ClassGroup
group c by new { c.CategoryName, c.CategoryWeight } into CategoryGroup
select new
{
CategoryName = CategoryGroup.Key.CategoryName,
CategoryWeight = CategoryGroup.Key.CategoryWeight,
Assignments = (from ct in CategoryGroup
group ct by new { ct.AssignmentName, ct.Score } into AssingnmentGroup
select new
{
AssignmentName = AssingnmentGroup.Key.AssignmentName,
Score = AssingnmentGroup.Key.Score
}).ToList()
}).ToList()
}).ToList()
}).ToList();
For example, if you want to access to the first Assignment's score, you can get it like this:
var student = data.FirstOrDefault();
var score = student.Classes[0].ClassCategories[0].Assignments[0].Score;
This is usually how I do It.
Create a class to store your data
Create a list of that class type
In your case instead of string dataRow maybe you can use a sub class
.
// get data from webservice
var json = webClient.DownloadString(url);
var values = JsonConvert.DeserializeObject<JArray>(json);
// create a list to save all the element
List<myClass> classList = new List<myClass>();
// process every row
foreach (string dataRow in values)
{
string[] dataField = dataRow.Split(',');
// have a constructor to assign each value to this element
myClass ctROW = new myClass(dataField);
classList.add(ctROW );

replacement for roundtrip serialize-deserialize

I've a table with over 100 column (including blobs) and I want to make a copy of object only with a few filled columns.
right now I'm doing it by selecting needed columns and doing a round-trip serialize and deserialize with Json.NET which is not efficient. what's the best way to handle this scenario?
BL.Case mCase;
BL.Case temp = db.Cases.Select(
xx => new
{
CaseID = xx.CaseID,
FirstName = xx.FirstName,
LastName = xx.LastName
}).FirstOrDefault(u => u.CaseID == CaseID);
mCase = Newtonsoft.Json.JsonConvert.DeserializeObject<BL.Case>(Newtonsoft.Json.JsonConvert.SerializeObject(temp));
Use AutoMapper.
Do something like this:
BL.Case mCase = null;
var temp = db.Cases.Select(
xx => new
{
CaseID = xx.CaseID,
FirstName = xx.FirstName,
LastName = xx.LastName
}).FirstOrDefault(u => u.CaseID == CaseID);
if (temp != null)
{
mCase = Mapper.DynamicMap<BL.Case>(temp);
}
Another solution that requires a bit more code (but might perform better) is to do the following:
In case you need a single item:
BL.Case mCase = null;
var temp = db.Cases.Select(
xx => new
{
CaseID = xx.CaseID,
FirstName = xx.FirstName,
LastName = xx.LastName
}).FirstOrDefault(u => u.CaseID == CaseID);
if (temp != null)
{
mCase = new Case()
{
CaseID = temp.CaseID,
FirstName = temp.FirstName,
LastName = temp.LastName,
};
}
If you need multiple items:
var temp = db.Cases.Select(
xx => new
{
CaseID = xx.CaseID,
FirstName = xx.FirstName,
LastName = xx.LastName
}); //Here you can filter your query if you want using Where
var result = temp
.ToList() //This will actually execute the query on the database
.Select(x => new Case() //Now you can do this since now we are working on in-memory data
{
CaseID = x.CaseID,
FirstName = x.FirstName,
LastName = x.LastName
});

Select from datatable with where clause

I am trying to select a few rows from a datatable. I am selecting two of the columns and how can I use a where clause in the below statement?
cars = new Head
{
heading = (string)dr["head"],
subHeads = dt.Select(r => new SubHead
{ // how to use a where clause here?
subHeading = (string)r["subhead"],
cars = dt.Select(r2 => new Cars
{ // how to use a where clause here?
name = (string)r2["name"],
quantity = (string)r2["qty"],
}).ToList()
}).ToList()
};
You could use,
dt.where(e => {check something}).Select({select code here})
Do this on both the places. Hope this helps.
You need to call the Where before you select. Example:
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
var contacts = ds.Tables["Contact"].AsEnumerable();
var orders = ds.Tables["SalesOrderHeader"].AsEnumerable();
var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.Field<Int32>("ContactID") == order.Field<Int32>("ContactID"))
&& order.Field<decimal>("TotalDue") < 500.00M)
.Select(order => new
{
ContactID = contact.Field<int>("ContactID"),
LastName = contact.Field<string>("LastName"),
FirstName = contact.Field<string>("FirstName"),
OrderID = order.Field<int>("SalesOrderID"),
Total = order.Field<decimal>("TotalDue")
}));
foreach (var smallOrder in query)
{
Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due: ${4} ",
smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName,
smallOrder.OrderID, smallOrder.Total);
}
taken from here.

How to order a IEnumerable<T> of anonymous type?

See the code below, I don't know why my ordering is not working, any ideas?
var orderSample = new { ProductName = "", Qty = 0, UserFullName = "" };
var ordersList = (new[] { orderSample }).ToList();
//loop thru another collection and fill ordersList by adding an order at a time
ordersList.Add(new { ProductName = "Product1", Qty = 5, UserFullName = "Mr. Smith" });
//sort the orders by name - DOESN'T WORK
ordersList.OrderBy(p => p.ProductName);
gvReport3.DataSource = ordersList;
gvReport3.DataBind();
var sortedList = ordersList.OrderBy(p => p.ProductName).ToList();
OrderBy() returns a sorted collection, it does not modify the ordersList.
If you need to modify the ordersList, use Sort instead.

Categories

Resources