I have a csv file that looks like this:
M;2017.12.01 17:04;1;example#example.com
T;1;K001;2
T;1;N001;1
M;2017.11.01 15:56;2;example#example.com
T;2;P001;2
T;2;P001;1
My problem is that I have to read this file into a List<> and be able to navigate in it with indexes but the different types of rows after the long ones are confusing me.
class Order
{
public string Type { get; set; }
public DateTime Date { get; set; }
public string OrderID { get; set; }
public string Email { get; set; }
public string ItemNumber { get; set; }
public int Quantity { get; set; }
public Order(string[] ordered , string[] items)
{
Type = ordered[0];
Date = DateTime.Parse(ordered[1]);
OrderID = ordered[2];
Email = ordered[3];
Type = items[0];
OrderID = items[1];
ItemNumber = items[2];
Quantity = int.Parse(items[3]);
}
}
class Program
{
static List<Order> orders = new List<Order>();
static void Main(string[] args)
{
Reading();
}
private static void Reading()
{
using (System.IO.StreamReader reader = new System.IO.StreamReader("orders.csv"))
{
while (!reader.EndOfStream)
{
orders.Add(new Order(reader.ReadLine().Split(';') , reader.ReadLine().Split(';')));
}
}
}
}
You can try to identify the line before creating it.
Than you can create two different methods to initialize your order.
while (!reader.EndOfStream)
{
var values = reader.ReadLine().Split(';');
if(DateTime.TryParse(values.Skip(1).First(), out var date)) {
orders.Add(Order.FromOrderWithDate(values));
}
else
orders.Last().Items.Add(Item.FromOrderWithEmail(values));
}
The two methods will be something like
public static Order FromRow(string[] ordered) =>
new Order {
Type = ordered[0],
Date = DateTime.Parse(ordered[1]),
OrderID = ordered[2],
Email = ordered[3],
Items = new List<Item>();
};
public static Item FromRow(string[] items) =>
new Item {
Type = items[0],
OrderID = items[1],
ItemNumber = items[2],
Quantity = int.Parse(items[3])
};
And finally two different class, one for order and one for item, the Order should contain a list for the items.
Try something like:
List<Customer> customers = new List<Customer>();
Customer lastCustomer = null;
foreach(var line in File.ReadLines("orders.csv"))
{
var values = line.Split(';');
if (values[0]=="M")
{
lastCustomer = new Customer(values);
customes.Add(lastCustomer);
}
else if (values[0]=="T" && lastCustomer != null)
{
lastCustomer.AddOrder(values);
}
}
(you'll need to write a Customer class that can construct its self from an array of strings, plus has a method for adding new Order objects to its own list of orders, again constructing them from an array)
Related
I need to sort my csv file alphabetically and not show the ones that it says "hidden" for (aka. client 4 and client 5) this is the code:
static void Main(string[] args)
{
ReadCSFVFile();
Console.WriteLine();
}
static void ReadCSFVFile()
{
var lines = File.ReadAllLines("Navigation.txt");
var list = new List<Company>();
foreach (var line in lines)
{
var values = line.Split(';' );
var company = new Company() { ID = values[0], MenuName = values[1], ParentID = values[2], IsHidden = values[3], LinkURL = values[4] };
list.Add(company);
}
list.ForEach(x => Console.WriteLine($"{x.ID}\t {x.MenuName}\t {x.ParentID}\t {x.IsHidden}\t {x.LinkURL}"));
}
public class Company
{
public string ID { get; set; }
public string MenuName { get; set; }
public string ParentID { get; set; }
public string IsHidden { get; set; }
public string LinkURL { get; set; }
}
and this is the csv file:
ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False; /company
2;About Us;1;False; /company/aboutus
3;Mission;1;False; /company/mission
4;Team;2;False; /company/aboutus/team
5;Client 2;10;False; /references/client2
6;Client 1;10;False; /references/client1
7;Client 4;10;True; /references/client4
8;Client 5;10;True; /references/client5
10;References;NULL;False; /references
The below should achieve this for you. I've commented the parts I've added to help out.
list.OrderBy(x => x.MenuName) // Order alphabetically based on MenuName
.Where(x => x.IsHidden != "True") // Filter only for non-hidden items
.ToList().ForEach(
x => Console.WriteLine($"{x.ID}\t {x.MenuName}\t {x.ParentID}\t{x.IsHidden}\t {x.LinkURL}"));
What is coming in to the webAPI is this JSON string and becomes deserilized in to this:
List<AddAssignMealView> mealtraiDeserializeObjects = JsonConvert.DeserializeObject<List<AddAssignMealView>>(mealTrainee);
mealtraiDeserializeObjects contains five index's one for each day of the week. Inside that specific index is a class that looks like what is below:
public class AddAssignMealView
{
public int TraineeID { get; set; }
public string DayOfTheWeek { get; set; }
public List<string> MealTypes { get; set; }
public List<int> MealID { get; set; }
public List<string> MealName { get; set; }
}
What I am trying to do is, be able to create a list of MealTrainee(Entity Framework):
public partial class MealTrainee
{
public int MealTraineeID { get; set; } //Ignore this one due to it being a post
public int MealID { get; set; }
public int TraineeID { get; set; }
public string DayOfTheWeek { get; set; }
public string MealType { get; set; }
public string MealName { get; set; }
public virtual Meal Meal { get; set; }
}
So I can be able to use addrange and add the list to the database. I understand how to use zip and combined two list types together if it is a single element. This is different due to it being a list of five elements and each of those five elements containing three lists. If someone could point me in the right direction, that would be helpful.
You can Zip 2 times to combine values from 3 lists into series of tuples. You can use SelectMany to flatten results. For example:
var result = mealtraiDeserializeObjects.SelectMany(c =>
c.MealID.Zip(c.MealName, (id,name) => new {id, name})
.Zip(c.MealTypes, (prev, type) => new {prev.id, prev.name, type})
.Select(r => new MealTrainee
{
TraineeID = c.TraineeID,
DayOfTheWeek = c.DayOfTheWeek,
MealID = r.id,
MealName = r.name,
MealType = r.type,
}));
This is the solution I found. I took it day by day in the sense that the first iteration would be Monday and then the count of meals(Ex:Meal for breakfast, Meal for Lunch) and put them in a count which would be "mealweek". Then I took mealweek and created a new mealtrainee for each count. Then after I made the meal out of the mealtrainee I put it in db.MealTrainees.AddRange and posted all the records.
[ResponseType(typeof(MealTrainee))]
public IHttpActionResult PostMealTrainee([FromBody] string mealTrainee)
{
List<MealTrainee> meals = new List<MealTrainee>();
using (DbContextTransaction dbContextTransaction = db.Database.BeginTransaction())
{
try
{
List<AddAssignMealView> mealtraiDeserializeObjects = JsonConvert.DeserializeObject<List<AddAssignMealView>>(mealTrainee);
foreach (var mealtraiDeserializeObject in mealtraiDeserializeObjects)
{
var mealWeek = mealtraiDeserializeObject.MealID.Select((m, i) => new
{
mealtraiDeserializeObject.TraineeID,
mealtraiDeserializeObject.DayOfTheWeek,
MealID = m,
MealTypes = mealtraiDeserializeObject.MealName[i],
MealName = mealtraiDeserializeObject.MealTypes[i]
}).ToList();
var meal = mealWeek.Select(x => new MealTrainee()
{
DayOfTheWeek = x.DayOfTheWeek,
MealID = x.MealID,
MealName = x.MealName,
MealType = x.MealTypes,
TraineeID = x.TraineeID
}).ToList();
db.MealTrainees.AddRange(meal);
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.SaveChanges();
dbContextTransaction.Commit();
return Ok(meals);
}
catch (Exception e)
{
dbContextTransaction.Rollback();
Logger.Log(e);
return BadRequest();
}
}
}
I have a custom list called ServiceFormFields and it has a List property named ChildrenTables.
I want to order descending by ChildrenTables' names
var fields = activeForm.ServiceFormFields.OrderBy (o =>
o.ChildrenTables.OrderBy(c=>c)).ToList();
but it does not work.
I want to order ServiceFormFields list according to its children. Maybe I should do with GroupBy..
So, for example...
ServiceFormFields has FieldName property.. and Children is a List of String
FieldName = Matter, Children = Version
FieldName = Client, Children = Matter
FieldName = Status, Children = Null
FieldName = Version, Children = Null (but has parents, it is Matter)
so and I want to order like:
2,1,3,4
because Client is the on highest level, second is Matter, third one is Version because Matter is its parent, and final is Status, because it does not have any dependency.
Edit: This is structure of the class
public class ServiceForm
{
public List<ServiceFormField> ServiceFormFields { get; set; }
public string Id { get; set; }
public bool IsDefaultPrimary { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string Type { get; set; }
}
public class ServiceFormField
{
public FormProperty FormField {get;set;}
public bool IsVisible { get;set;}
public List<string> ParentTables { get; set; }
public bool HasChildren { get; set; }
public List<string> ChildrenTables { get; set; }
}
public partial class FormProperty
{
private string NameField;
private string SQLInfoField;
...
}
NameField contains Client, Matter, Version..
I don't know if this is what you want, but here's a solution with the least bits of code needed.
I just transformed all the Childrens into a single string, and then the order by compares against them.
This is certainly not the most performance-wise solution, if this matters.
here's a sample:
static void Main(string[] args)
{
ServiceForm sf = new ServiceForm();
sf.ServiceFormFields = new List<ServiceFormField>
{
new ServiceFormField { ChildrenTables = new List<string> { "a", "b", "c"}},
new ServiceFormField { ChildrenTables = new List<string> { "tra la la", "xxx"}},
new ServiceFormField { ChildrenTables = new List<string> { "TTTTT" }},
new ServiceFormField { ChildrenTables = new List<string> { "123455", "8157125", "1763123"}},
new ServiceFormField { ChildrenTables = new List<string> { " ", " ", " ", " "}}
};
var ordered= sf.ServiceFormFields.OrderByDescending(f => string.Join(",", f.ChildrenTables)).ToList();
foreach(ServiceFormField sff in ordered)
{
foreach(string s in sff.ChildrenTables)
{
Console.WriteLine(s);
}
}
}
Output:
There are two ways, first you can use the List.Sort function with the signature Comparison like:
activeForm.ServiceFormFields.Sort(new Comparison<ServiceFormField>((e1,e2)=>Compare(e1, e2)));
private static int Compare(ServiceFormField e1, ServiceFormField e2)
{
//Do your logic here
}
in this case you will have an inplace sort.
or use the linq orderby function with the signature to KeySelector, an IComparer and implement an IComparer like that
var result = activeForm.ServiceFormFields.OrderBy(e => e, new ServiceFormFieldComparer());
public class ServiceFormFieldComparer : IComparer<ServiceFormField>
{
private int Compare(ServiceFormField e1, ServiceFormField e2)
{
//Your logic here
}
}
in this case you will have an ordered list returned to you
I need to generate identical ids for 2 objects for every loop. Do I need to make another loop specifcally for the ids? There wont be more than 20 objects created at a time so worrying about collisions isn't a big concern. nothing is being saved to a database.
I need to generate a matching uid for productsId and Id
public class data
{
public int productsId { get; set; }
public string sqft { get; set; }
public string price { get; set; }
}
public class products
{
public int Id { get; set; }
public string product { get; set; }
}
public class LegendModel
{
public string Color { get; set; }
public string Name { get; set; }
public IList<data> Data { get; set; }
public IList<products> Products { get; set; }
}
public class ExportLegendController : ApiController
{
// POST: api/ExportLegend
[HttpPost]
public PDF Post([FromBody]List<LegendModel> legendModel)
{
try
{
var subjectProperty = legendModel[legendModel.Count - 1];
var xEleLegend = new XElement("Legend",
from item in legendModel
select new XElement("item",
new XElement("Name", item.Name),
new XElement("Color", item.Color)
));
// Save the document...
var dt = DateTime.Now.ToString("g").Replace('/', '-').Replace(':', '-');
var filename = string.Format("{0}-{1}.xml", "Legend", dt);
string physicalPath = HttpContext.Current.Server.MapPath("/legendXmls");
string relativePath = Path.Combine(physicalPath, filename).Replace("\\", "/");
var pdfName = relativePath;
xEleLegend.Save(pdfName);
var data = new List<data>();
var products = new List<products>();
foreach (var item in subjectProperty.Data)
{
data.Add(new data
{
productsId = item.productsId,
sqft = item.sqft,
price = item.price
});
}
foreach (var item in subjectProperty.Products)
{
products.Add(new products
{
Id = item.Id,
product = item.product
});
};
var xEleProperty = new XElement("Property",
from d in data
join product in products on d.productsId equals product.Id
select new XElement("Points",
new XElement("Sqft", d.sqft),
new XElement("Price", d.price),
new XElement("Product", product.product)
));
Unique ID generation using GUID and Cryptography
Using GUID:
public string generateID()
{
return Guid.NewGuid().ToString("N");
}
"N" - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (32 digits)
Or
using System.Security.Cryptography; // Import this Dll
public string Get8Digits()
{
var bytes = new byte[4];
var rng = RandomNumberGenerator.Create();
rng.GetBytes(bytes);
uint random = BitConverter.ToUInt32(bytes, 0) % 100000000;
return String.Format("{0:D8}", random);
}
I have the following classes:
public class Products
{
public int Id { get; set; }
public string ProductName { get; set; }
public int Price { get; set; }
public IList<ProductFiles> ProductFiles { get; set; }
}
public class ProductFiles
{
public int NumberOfFiles { get; set; }
public int NumberOfShops { get; set; }
}
Here I am trying to set the value of the property NumberOfFiles (member of ProductFiles):
public Products CountProductFiles(int productId)
{
DB_utilities db = new DB_utilities();
object[] spParams = new object[] { productId};
Products product = new Products();
using (var reader = db.procSelect("[Products_CountFiles]", spParams))
{
reader.Read();
{
Products _products = new Products
{
ProductName = (string)reader["ProductName"],
Price = (double)reader["Price"],
// I am trying to do something like this but this does not work:
ProductFiles.NumberOfFiles = (int)reader["NumberOfFiles"]
};
Products = _products ;
}
}
return Products;
}
How can I set the value of the prop NumberOfFiles? Or is the entire concept wrong?
As ProductFiles is a collection of ProductFiles, you need to do this
Products p = new Products();
p.ProductFiles = new List<ProductFiles>();
p.ProductFiles.Add(new ProductFiles() { NumberOfFiles = 1 }); // or some other defined value.
The collection needs to be initialized before adding objects to it.
In your example, you do not treat ProductFiles as a collection, that is why you get the error.
The point of a collection is that it can contain zero or more items. You need to put an instance in the collection. You also need to create a collection to ensure that the collection itself is not null.
new Product {
Files = new List<ProductFile>() {
new ProductFile {
FileCount = 42
}
}
}
In this example, Products::ProductFiles is a list.
Hence, in order to set the value of NumberOfFiles, do something like this:
ProductFiles[i].NumberOfFiles = //whatever you want to set it to.
But, before doing all this, do not forget to instantiate the List variable. Otherwise, it will itself be NULL.