Combine contents of two files using LINQ to CSV - c#

This is probably a dumb question, but I'm wondering how I would fill a list with the following data for a CSV file.
Here's the code so far,
class Info
{
[CsvColumn(Name = "Lease Name", FieldIndex = 1)]
public string leaseName2 { get; set; }
[CsvColumn(Name = "Field Name", FieldIndex = 2)]
public string fieldName2 { get; set; }
[CsvColumn(Name = "Reservoir", FieldIndex = 3)]
public string reservoir2 { get; set; }
[CsvColumn(Name = "Operator", FieldIndex = 4)]
public string operator2 { get; set; }
[CsvColumn(Name = "County", FieldIndex = 5)]
public string county2 { get; set; }
[CsvColumn(Name = "State", FieldIndex = 6)]
public string state2 { get; set; }
[CsvColumn(Name = "Majo", FieldIndex = 7)]
public string majo2 { get; set; }
[CsvColumn(Name = "Resv Cat", FieldIndex = 8)]
public string resvCat2 { get; set; }
[CsvColumn(Name = "Discount Rate", FieldIndex = 9)]
public double disRate2 { get; set; }
There are more columns I just did not want to list them all because that would be redundant. If anyone could help that would be greatly appreciated.

#GertArnold is right, you can use Union(). I used Concat() in my example. Union returns distinct records, Concat doesn't.
using System;
using System.Collections.Generic;
using System.Linq;
using LINQtoCSV;
namespace LinqCsvSandbox
{
class SampleData
{
[CsvColumn(Name = "ID", FieldIndex = 1)]
public string ID { get; set; }
[CsvColumn(Name = "PersonName", FieldIndex = 2)]
public string Name { get; set; }
public override string ToString()
{
return string.Format("{0}: {1}", ID, Name);
}
}
class Program
{
static void Main(string[] args)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = false,
FileCultureName = "en-us",
EnforceCsvColumnAttribute = true,
};
CsvContext cc = new CsvContext();
IEnumerable<SampleData> data1 = cc.Read<SampleData>("File1.csv", inputFileDescription);
IEnumerable<SampleData> data2 = cc.Read<SampleData>("File2.csv", inputFileDescription);
IEnumerable<SampleData> all = data1.Concat(data2);
// Uncomment to see the items printed
//foreach(var item in all)
// Console.WriteLine(item);
cc.Write(all, "All.csv");
}
}
}
In my example, File1.csv contains
1,Fred
2,Wilma
File2.csv contains
3,Tango
4,Cash
And the resulting All.csv contains
ID,PersonName
1,Fred
2,Wilma
3,Tango
4,Cash
For those unfamiliar, Linq to CSV is available as a package from NuGet.

Here are a couple of ways, the first is just using an object initializer, the second transfers a small list to the your CSV list:
List<Info> infoList = new List<Info>
{
new Info()
{
leaseName2 = "x"
}
};
List<string> stringList = new List<string> {"xy", "zz"};
infoList.AddRange(stringList.Select(stringVal => new Info()
{
leaseName2 = stringVal
}));

Related

How do I copy two similar lists?

I have two lists. There are only one field difference. How to fill the lists with each other.
[Serializable()]
public class Lst1
{
public string filed1 { get; set; }
public Int16 filed2 { get; set; }
.
.
.
public Boolean filed100 { get; set; }
}
[Serializable()]
public class Lst2
{
public string filed1 { get; set; }
public Int16 filed2 { get; set; }
.
.
.
public Boolean filed100 { get; set; }
public string filed101 { get; set; }
}
List<Lst1> Lst1_ = new List<Lst1>();
List<Lst2> Lst2_ = new List<Lst2>();
I fill out lists from files.
then,I need to fill out the list two from list one,There are many fields And I do not want to use the foreach loop.
It should be remembered that my previous class was already built and serialized and stored in a file. And now I need to transfer the previous information to the second class structure.
I do not want to use this loop!
foreach (var t in Lst1_)
{
Lst2_.Add(new lst2
{
filed1 = t.filed1,
filed2 = t.filed2,
.
.
.
filed100 = t.filed100,
filed101 = "kk"
}
}
Is this what you want?
class Lst1
{
public string filed1 { get; set; }
public string filed2 { get; set; }
public string filed3 { get; set; }
public string filed4 { get; set; }
public string filed5 { get; set; }
}
class Lst2
{
public string filed1 { get; set; }
public string filed2 { get; set; }
public string filed3 { get; set; }
public string filed4 { get; set; }
public string filed5 { get; set; }
public string filed6 { get; set; }
}
void CopyData()
{
// test data
List<Lst1> Lst1_ = new List<Lst1>()
{
new Lst1()
{
filed1 = "1",
filed2 = "2",
filed3 = "3",
filed4 = "4",
filed5 = "5",
},
new Lst1()
{
filed1 = "6",
filed2 = "7",
filed3 = "8",
filed4 = "9",
filed5 = "10",
},
};
List<Lst2> Lst2_ = new List<Lst2>();
foreach (var item in Lst1_)
{
Type type1 = item.GetType();
PropertyInfo[] properties1 = type1.GetProperties();
var current = new Lst2();
Type type2 = current.GetType();
PropertyInfo[] properties2 = type2.GetProperties();
int k = 0;
foreach (PropertyInfo property in properties1)
{
var value = property.GetValue(item, null);
int n;
bool isNumeric = int.TryParse(value.ToString(), out n);
if (!isNumeric)
value = "Your desired value";
properties2[k].SetValue(current, value);
k++;
}
Lst2_.Add(current);
}
}
It copies everything from list 1 to list2.
No need to waste your time and money, AutoMapper can do it for you with only 2 lines of code:
using AutoMapper;
namespace ConsoleApp39
{
class Program
{
static void Main (string[] args)
{
// fill list1 with data
var list1 = new List1
{
Field1 = "test",
Field2 = 5,
Field3 = false,
};
// 1) configure auto mapper
Mapper.Initialize (cfg => cfg.CreateMap<List1, List2> ());
// 2) create list2 and fill with data from list1
List2 list2 = Mapper.Map<List2> (list1);
// fill extra fields
list2.Field4 = new byte[] { 1, 2, 3 };
}
}
public class List1
{
public string Field1 { get; set; }
public int Field2 { get; set; }
public bool Field3 { get; set; }
}
public class List2
{
public string Field1 { get; set; }
public int Field2 { get; set; }
public bool Field3 { get; set; }
public byte[] Field4 { get; set; } // extra field
}
}
Do Lst1 can inheritance from Lst2?
Something like this,
the two lists:
[Serializable()]
public class Lst1
{
public string filed1 { get; set; }
public int filed2 { get; set; }
public bool filed100 { get; set; }
}
[Serializable()]
public class Lst2 : Lst1
{
public string filed101 { get; set; }
}
and one print extension
public static class CExtensions
{
public static string PropertyList(this Lst1 obj)
{
var props = obj.GetType().GetProperties();
var sb = new StringBuilder();
foreach (var p in props)
{
sb.AppendLine(p.Name + ": " + p.GetValue(obj, null));
}
return sb.ToString();
}
}
then using it:
class Program
{
static void Main(string[] args)
{
const int I = 15;
try
{
//init first list
List<Lst1> Lst1_ = new List<Lst1>();
Init(Lst1_);
//print it
Console.WriteLine("Lst1_");
Console.WriteLine(new string('-', I));
Lst1_.ForEach(x => Console.WriteLine(x.PropertyList()));
Console.WriteLine(new string('=', I));
Console.ReadKey();
//init second list
List<Lst1> Lst2_ = Lst1_.Cast<Lst1>().ToList(); //equivalent of two next lines
//List<Lst1> Lst2_ = new List<Lst2>().ConvertAll(x => (Lst1)x);
//Lst2_.AddRange(Lst1_);
//add one more
Lst2_.Add(new Lst2
{
filed1 = "101",
filed2 = 202,
filed100 = true,
filed101 = "10100"
});
//and print
Console.WriteLine("Lst2_");
Console.WriteLine(new string('-', I));
Lst2_.ForEach(x => Console.WriteLine(x.PropertyList()));
Console.WriteLine(new string('=', I));
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadKey();
}
}
private static void Init(List<Lst1> lst_)
{
for (int i = 1; i <= 3; i++)
{
lst_.Add(new Lst1
{
filed1 = i.ToString(),
filed2 = 2 * i,
filed100 = i % 2 == 0
});
}
}
}
enjoy =)

Read hierarchical XML and flatten into a List of objects?

I have an XML document which I would like to read, flatten, and return a List<> of an object I called PMRow. For each CodingRow in the XML there should be a corresponding List element and the "header" details of the Invoice should simply repeat. Many of the XML elements will be ignored in this transformation at both the Invoice and CodingRow level. I've started writing the code below and I can't conceptualize where to go from there:
Incomplete code:
public static List<PMRow> ParseToPMRows(FileInfo myFile)
{
var xDoc = XDocument.Load(myFile.FullName);
var query = from element in xDoc.Element("InvoiceDocument").Element("Invoice").Element("CodingRows").Elements("CodingRow")
select
}
Target Class:
public class PMRow
{
public string SupplierCode { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public decimal GrossSum { get; set; }
public decimal NetSum { get; set; }
public decimal TaxSum { get; set; }
public decimal CR_GrossSum { get; set; }
public string AccountNumber { get; set; }
public string DimCode1 { get; set; }
}
XML Docuemnt:
<InvoiceDocument>
<Method>Post</Method>
<Invoice>
<GrossSum nil="False">700.000000</GrossSum>
<InvoiceDate nil="False">2018-09-26</InvoiceDate>
<InvoiceNumber nil="False">180928003802901</InvoiceNumber>
<NetSum nil="False">700.000000</NetSum>
<PaidSum nil="False">0.000000</PaidSum>
<PaymentBlock nil="False">false</PaymentBlock>
<PaymentDate nil="False">0001-01-01</PaymentDate>
<SupplierCode nil="False">AQUINC</SupplierCode>
<SupplierParentId nil="False"></SupplierParentId>
<TaxCode nil="False"></TaxCode>
<TaxPercent nil="False">0.000000</TaxPercent>
<TaxPercent2 nil="False">0.000000</TaxPercent2>
<TaxSum nil="False">0.000000</TaxSum>
<OrderNumber nil="False"></OrderNumber>
<OrderInCoding nil="False" />
<CodingRows>
<CodingRow>
<GrossSum nil="False">500.000000</GrossSum>
<InternalStatus nil="False">Loaded</InternalStatus>
<AccountCode nil="False">1990</AccountCode>
<AccountName nil="False">Gain on Non-Operating Asset</AccountName>
<DimCode1 nil="False">01</DimCode1>
<DimName1 nil="False">Operating/Unrestricted</DimName1>
<MaterialGroup nil="False"></MaterialGroup>
<FiscalYear nil="False"></FiscalYear>
<DimCode3 nil="False">06</DimCode3>
<DimName3 nil="False">Sports</DimName3>
<DimCode4 nil="False">06500</DimCode4>
<DimName4 nil="False">Personal Training</DimName4>
<DimCode5 nil="False">6</DimCode5>
<DimName5 nil="False">Minneapolis</DimName5>
<DimCode6 nil="False"></DimCode6>
<DimName6 nil="False"></DimName6>
</CodingRow>
<CodingRow>
<GrossSum nil="False">200.000000</GrossSum>
<InternalStatus nil="False">Loaded</InternalStatus>
<AccountCode nil="False">2390</AccountCode>
<AccountName nil="False">Gain on Non-Operating Asset</AccountName>
<DimCode1 nil="False">02</DimCode1>
<DimName1 nil="False">Operating/Unrestricted</DimName1>
<MaterialGroup nil="False"></MaterialGroup>
<FiscalYear nil="False"></FiscalYear>
<DimCode3 nil="False">06</DimCode3>
<DimName3 nil="False">Sports</DimName3>
<DimCode4 nil="False">06500</DimCode4>
<DimName4 nil="False">Personal Training</DimName4>
<DimCode5 nil="False">6</DimCode5>
<DimName5 nil="False">Minneapolis</DimName5>
<DimCode6 nil="False"></DimCode6>
<DimName6 nil="False"></DimName6>
</CodingRow>
</CodingRows>
<InvoiceRows />
</Invoice>
Conceptualized target (two objects of type PMRow in a List):
AQUINC, 180928003802901, 9/26/2018, 700, 700, 0, 500, 1990, 01
AQUINC, 180928003802901, 9/26/2018, 700, 700, 0, 200, 2390, 02
Is this what you are looking for?
XElement invoice = xDoc.Root.Element("Invoice");
List<PMRow> rows = invoice
.Element("CodingRows")
.Elements("CodingRow")
.Select(codingRow => new PMRow
{
SupplierCode = invoice.Element("SupplierCode").Value,
InvoiceNumber = invoice.Element("InvoiceNumber").Value,
InvoiceDate = DateTime.Parse(invoice.Element("InvoiceDate").Value),
GrossSum = decimal.Parse(invoice.Element("GrossSum").Value),
NetSum = decimal.Parse(invoice.Element("NetSum").Value),
TaxSum = decimal.Parse(invoice.Element("TaxSum").Value),
CR_GrossSum = decimal.Parse(codingRow.Element("GrossSum").Value),
AccountNumber = codingRow.Element("AccountCode").Value,
DimCode1 = codingRow.Element("DimCode1").Value,
})
.ToList();
Note: the above assumes that all elements will be present and valid. If this is not the case, you will need to add appropriate handling for that.
Fiddle: https://dotnetfiddle.net/DjKcDg
I generally find more convenient to first serialize XML into class object and then iterate over serialzed object.
You only need
XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
TextReader reader = new StringReader(source);
Invoice resultObj = (Invoice)serializer.Deserialize(reader);
var pmRows = resultObj.CodingRows.CodingRow.Select(item => new PMRow
{
CR_GrossSum = Convert.ToDecimal(resultObj.GrossSum.Text),
InvoiceDate = Convert.ToDateTime(resultObj.InvoiceDate.Text),
InvoiceNumber = resultObj.InvoiceNumber.Text,
SupplierCode = resultObj.SupplierCode.Text,
NetSum = Convert.ToDecimal(resultObj.NetSum.Text),
GrossSum = Convert.ToDecimal(resultObj.GrossSum.Text),
TaxSum = Convert.ToDecimal(resultObj.TaxSum.Text),
AccountNumber = item.AccountCode.Text,
DimCode1 = item.DimCode1.Text
}).ToList();
you can check my solution from https://dotnetfiddle.net/jr11hB
Try code below. I assumed they are multiple Invoice in a file so I had to use SelectMany() to return a flat list
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication75
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
ParseToPMRows(FILENAME);
}
public static List<PMRow> ParseToPMRows(string myFile)
{
XDocument xDoc = XDocument.Load(myFile);
var tempResults = xDoc.Descendants("Invoice").Select(x => new {
supplierCode = (string)x.Element("SupplierCode"),
invoiceNumber = (string)x.Element("InvoiceNumber"),
invoiceDate = (DateTime)x.Element("InvoiceDate"),
grossSum = (decimal)x.Element("GrossSum"),
netSum = (decimal)x.Element("NetSum"),
taxSum = (decimal)x.Element("TaxSum"),
codingRows = x.Descendants("CodingRow").Select(y => new {
crGrossSum = (decimal)y.Element("GrossSum"),
accounNumber = (string)y.Element("AccountCode"),
dimCode1 = (string)y.Element("DimCode1")
}).ToList()
}).ToList();
List<PMRow> rows = tempResults.Select(x => x.codingRows.Select(y => new PMRow()
{
SupplierCode = x.supplierCode,
InvoiceNumber = x.invoiceNumber,
InvoiceDate = x.invoiceDate,
GrossSum = x.grossSum,
NetSum = x.netSum,
TaxSum = x.taxSum,
CR_GrossSum = y.crGrossSum,
AccountNumber = y.accounNumber,
DimCode1 = y.dimCode1
})).SelectMany(x => x).ToList();
return rows;
}
}
public class PMRow
{
public string SupplierCode { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public decimal GrossSum { get; set; }
public decimal NetSum { get; set; }
public decimal TaxSum { get; set; }
public decimal CR_GrossSum { get; set; }
public string AccountNumber { get; set; }
public string DimCode1 { get; set; }
}
}
For one Invoice you can use following :
public static List<PMRow> ParseToPMRows(string myFile)
{
XDocument xDoc = XDocument.Load(myFile);
var tempResults = xDoc.Descendants("Invoice").Select(x => new {
supplierCode = (string)x.Element("SupplierCode"),
invoiceNumber = (string)x.Element("InvoiceNumber"),
invoiceDate = (DateTime)x.Element("InvoiceDate"),
grossSum = (decimal)x.Element("GrossSum"),
netSum = (decimal)x.Element("NetSum"),
taxSum = (decimal)x.Element("TaxSum"),
codingRows = x.Descendants("CodingRow").Select(y => new {
crGrossSum = (decimal)y.Element("GrossSum"),
accounNumber = (string)y.Element("AccountCode"),
dimCode1 = (string)y.Element("DimCode1")
}).ToList()
}).FirstOrDefault();
List<PMRow> rows = tempResults.codingRows.Select(x => new PMRow()
{
SupplierCode = tempResults.supplierCode,
InvoiceNumber = tempResults.invoiceNumber,
InvoiceDate = tempResults.invoiceDate,
GrossSum = tempResults.grossSum,
NetSum = tempResults.netSum,
TaxSum = tempResults.taxSum,
CR_GrossSum = x.crGrossSum,
AccountNumber = x.accounNumber,
DimCode1 = x.dimCode1
}).ToList();
return rows;
}

Hierarchical List Conversion For Tree View

I have a list of records that I want to display in a tree view, it has a structure like this.
public class Account
{
public string ac_cd { get; set; }
public string ac_name { get; set; }
public string ac_parent_cd { get; set; }
public string ac_nature { get; set; }
}
I want to convert this into a hierarchical structure, so assuming I have a list that contains the following items,
ac_cd = "01", ac_name = "Cash", ac_parent_cd = "Null", ac_nature = "Asset",
ac_cd = "02", ac_name = "Supplies", ac_parent_cd = "Null", ac_nature = "Asset",
ac_cd = "01.01", ac_name = "ACME Corp Cash", ac_parent_cd = "01", ac_nature = "Asset",
ac_cd = "02.01", ac_name = "ACME Corp Supplies", ac_parent_cd = "02", ac_nature = "Expense",
ac_cd = "02.01.01", ac_name = "ACME Corp Office Supplies", ac_parent_cd = "02.01", ac_nature = "Expense",
I want to convert these items into a hierarchical list like this,
01-Cash
---01.01-ACME Corp Cash
02-Supplies
---02.01-ACME Corp Supplies
------02.01.01-ACME Corp Office Supplies
There can be many number of levels for an account. My code so far can only make a child account up to a single level. Here is the POCO of the hierarchical model.
public class AccountTree
{
public string ac_info { get; set; }
public List<ChildAccount> ac_child { get; set; }
}
public class ChildAccount
{
public string ac_info { get; set; }
}
And here is my code.
List<Account> accountList = new List<Account>();
accountList.Add(new Account { ac_cd = "01", ac_name = "Cash", ac_parent_cd = "", ac_nature = "Asset" });
accountList.Add(new Account { ac_cd = "01.01", ac_name = "Abc Cash", ac_parent_cd = "01", ac_nature = "Asset" });
accountList.Add(new Account { ac_cd = "01.02", ac_name = "Xyz Cash", ac_parent_cd = "01", ac_nature = "Asset" });
List<AccountTree> targetAccountList = new List<AccountTree>();
List<ChildAccount> childAccountList = new List<ChildAccount>();
foreach (var childAccountGrouping in accountList.GroupBy(o => new { o.ac_parent_cd }))
{
if (childAccountGrouping.Key.ac_parent_cd == "")
{
continue;
}
var childAccounts = childAccountGrouping.ToList();
foreach (var childAccount in childAccounts)
{
childAccountList.Add(new ChildAccount { ac_info = childAccount.ac_cd + childAccount.ac_name + childAccount.ac_nature });
}
var parentAccount = (from p in accountList
where p.ac_cd == childAccountGrouping.Key.ac_parent_cd
select p).Single();
AccountTree accountTree = new AccountTree
{
ac_info = parentAccount.ac_cd + parentAccount.ac_name + parentAccount.ac_nature,
ac_child = childAccountList
};
targetAccountList.Add(accountTree);
}
I'm pretty sure I'll have to change this significantly. Would appreciate any assistance.
I would just change the Account class like that:
public class Account
{
public string ac_cd { get; set; }
public string ac_name { get; set; }
public Account ac_parent_cd { get; set; }
public List<Account> ac_children_cd { get; set; }
public string ac_nature { get; set; }
}
now you have easy access to parent and children of all elements

Specified cast is not valid with field int

I keep getting the "Specified cast is not valid" error on my code in the ID column. I tried using
data.Columns.Add("ID", typeof(int));
to let it know that the ID is an int. I am getting the data from a local .xls file.
here's my code:
foreach (DataRow p in data.Rows)
{
TopPlayed top = new TopPlayed()
{
TrackID = p.Field<int>("ID"),
TrackName = p.Field<string>("Track Name"),
ArtistName = p.Field<string>("Artist Name"),
Times = p.Field<double>("NoOfPlays").ToString()
};
data.Columns.Add("ID", typeof(int));
daa.Add(top);
}
here's my class:
public class TopPlayed
{
public int TrackID { get; set; }
public string TrackName { get; set; }
public string ArtistName { get; set; }
public string Times { get; set; }
}
cheers in advance :)
Try using double instead. Excel exposes data as either strings or doubles.
TopPlayed top = new TopPlayed()
{
TrackID = Convert.ToInt32(p.Field<double>("ID")),
TrackName = p.Field<string>("Track Name"),
ArtistName = p.Field<string>("Artist Name"),
Times = p.Field<double>("NoOfPlays").ToString()
};
Another way is checking our type (with Watch window for example):
row.Table.Columns["id"].DataType
What print (in my case): {Name = "Int64" FullName = "System.Int64"}, then change your code to proper type:
TopPlayed top = new TopPlayed()
{
TrackID = p.Field<Int64>("ID"),
TrackName = p.Field<string>("Track Name"),
ArtistName = p.Field<string>("Artist Name"),
Times = p.Field<double>("NoOfPlays").ToString()
};
public class TopPlayed
{
public Int64 TrackID { get; set; }
public string TrackName { get; set; }
public string ArtistName { get; set; }
public string Times { get; set; }
}
If it's a query inside a query, the best way that worked is to submit a simple String
and then to convert it to int32 ex.:
instead of using
p.Field("ID")
do it
row["ID"]

How can I modify my code to get data from an array of objects in C#?

I have the following class:
public partial class Content
{
public int ContentId { get; set; }
public int ContentTypeId { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public System.DateTime ModifiedDate { get; set; }
public virtual byte[] Version { get; set; }
public int SubjectId { get; set; }
public virtual Subject Subject { get; set; }
}
and the following code:
var myData = new[] {
"Content 1",
"Content 2",
"Content 3"
};
var contents = myData.Select(e => new Content
{
Title = e,
Text = "xx",
ModifiedDate = DateTime.Now
}
How can I modify this so I can specify both the "Title" and some small sample "Text" in the myData array. I was thinking about using an array of objects but I am not quite sure how to set this up.
Here is the syntax for that:
var myData = new List<Content>
{
new Content{Title = "Content 1", Text = "xx", ModifiedDate = DateTime.Now},
new Content{Title = "Content 2", Text = "AB", ModifiedDate = DateTime.Now},
new Content{Title = "Content 3", Text = "CC", ModifiedDate = DateTime.Now}
};
How about you use Tuple?
var myDatas = new[]
{
new Tuple<string, string, DateTime>("Title", "Example", DateTime.Now),
new Tuple<string, string, DateTime>("Title2", "Example", DateTime.Now.AddDays(-1)),
new Tuple<string, string, DateTime>("Title3", "Example", DateTime.Now.AddDays(1))
};
var contents = myDatas.Select(e => new Content
{
Title = e.Item1,
Text = e.Item2,
ModifiedDate = DateTime.Now
});

Categories

Resources