Iterate through array and add up elements with a control break logic - c#

I am trying to use this for each loop to iterate through this array, and sum up the elements, 12, 10, 8, 7, and 6, when element 0 is not different (position.account), using control break logic,
This is furthering on a question I had earlier, but I can't seem to figure out how to do this logically.
static void Main(string[] args)
{
String path = #"C:\Users\jhochbau\documents\visual studio 2015\Projects\CsvReader\CsvReader\Position_2016_02_25.0415.csv";
//Adding lines read into a string[];
string[] lines = File.ReadAllLines(path);
foreach(string line in lines)
{
Positions position = new Positions();
string[] parsedLine = line.Split(',');
position.account = parsedLine[0];
position.settleMM = parsedLine[10];
position.open = parsedLine[6];
position.buy = parsedLine[7];
position.sell = parsedLine[8];
position.underlying = parsedLine[12];
//Need to convert these to an int.
//for each iteration through the loop where string[0] is already existing
//I want to have sum = sum + string[10]
}
Console.Read();
}
public class Positions
{
public string account { get; set; }
public string symbol { get; set; }
public string prevClose { get; set; }
public string curPrx { get; set; }
public string settlePX { get; set; }
public string Mult { get; set; }
public string open { get; set; }
public string buy { get; set; }
public string sell { get; set; }
public string netMM { get; set; }
public string settleMM { get; set; }
public string settleDay { get; set; }
public string underlying { get; set; }
}

Further to my comment, you could do something like this:
// store the accounts inside this dictionary
var accounts = new Dictionary<string, Positions>();
foreach(string line in lines)
{
Positions position = new Positions();
string[] parsedLine = line.Split(',');
position.account = parsedLine[0];
...
Positions existingAccount;
// if the account already exists in the dictionary
if (accounts.TryGetValue(position.account, out existingAccount)) {
existingAccount.buy += position.buy;
// do updating logic here
} else {
accounts.add(position.account, position);
// otherwise add it as a new element
}
}

Alternatively, you could go for Linq:
File.ReadLines(path)
.Select( line => new Position(line) )
.GroupBy( position => position.account )
.Select( group => new { Account = group.Key,
Sum = group.Select( position => position.settleMM ).Sum() } );

Related

Map one class data to another class with iteration

I have a C# project and looking for simple solution for map one class object data to list of another class object.
This is my input class
public class RatesInput
{
public string Type1 { get; set; }
public string Break1 { get; set; }
public string Basic1 { get; set; }
public string Rate1 { get; set; }
public string Type2 { get; set; }
public string Break2 { get; set; }
public string Basic2 { get; set; }
public string Rate2 { get; set; }
public string Type3 { get; set; }
public string Break3 { get; set; }
public string Basic3 { get; set; }
public string Rate3 { get; set; }
}
This is my another class structure
public class RateDetail
{
public string RateType { get; set; }
public decimal Break { get; set; }
public decimal Basic { get; set; }
public decimal Rate { get; set; }
}
it has a object like below. (For easiering the understanding, I use hardcoded values and actually values assign from a csv file)
RatesInput objInput = new RatesInput();
objInput.Type1 = "T";
objInput.Break1 = 100;
objInput.Basic1 = 50;
objInput.Rate1 = 0.08;
objInput.Type2 = "T";
objInput.Break2 = 200;
objInput.Basic2 = 50;
objInput.Rate2 = 0.07;
objInput.Type3 = "T";
objInput.Break3 = 500;
objInput.Basic3 = 50;
objInput.Rate3 = 0.06;
Then I need to assign values to "RateDetail" list object like below.
List<RateDetail> lstDetails = new List<RateDetail>();
//START Looping using foreach or any looping mechanism
RateDetail obj = new RateDetail();
obj.RateType = //first iteration this should be assigned objInput.Type1, 2nd iteration objInput.Type2 etc....
obj.Break = //first iteration this should be assigned objInput.Break1 , 2nd iteration objInput.Break2 etc....
obj.Basic = //first iteration this should be assigned objInput.Basic1 , 2nd iteration objInput.Basic2 etc....
obj.Rate = //first iteration this should be assigned objInput.Rate1, 2nd iteration objInput.Rate2 etc....
lstDetails.Add(obj); //Add obj to the list
//END looping
Is there any way to convert "RatesInput" class data to "RateDetail" class like above method in C#? If yes, how to iterate data set?
Try this:
public class RatesList : IEnumerable<RateDetail>
{
public RatesList(IEnumerable<RatesInput> ratesInputList)
{
RatesInputList = ratesInputList;
}
private readonly IEnumerable<RatesInput> RatesInputList;
public IEnumerator<RateDetail> GetEnumerator()
{
foreach (var ratesInput in RatesInputList)
{
yield return new RateDetail
{
RateType = ratesInput.Type1,
Break = Convert.ToDecimal(ratesInput.Break1, new CultureInfo("en-US")),
Basic = Convert.ToDecimal(ratesInput.Basic1, new CultureInfo("en-US")),
Rate = Convert.ToDecimal(ratesInput.Rate1, new CultureInfo("en-US"))
};
yield return new RateDetail
{
RateType = ratesInput.Type2,
Break = Convert.ToDecimal(ratesInput.Break2),
Basic = Convert.ToDecimal(ratesInput.Basic2),
Rate = Convert.ToDecimal(ratesInput.Rate2, new CultureInfo("en-US"))
};
yield return new RateDetail
{
RateType = ratesInput.Type3,
Break = Convert.ToDecimal(ratesInput.Break3),
Basic = Convert.ToDecimal(ratesInput.Basic3),
Rate = Convert.ToDecimal(ratesInput.Rate3, new CultureInfo("en-US"))
};
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
And use:
var list = new RatesList(new List<RatesInput>() { objInput });
foreach (var item in list)
{
Console.WriteLine(item.Basic);
}
You can use Reflection to get the properties info like this:
var props = objInput.GetType().GetProperties();
var types = props.Where(x => x.Name.StartsWith("Type"))
.Select(x => x.GetValue(objInput)).ToList();
var breaks = props.Where(x => x.Name.StartsWith("Break"))
.Select(x => x.GetValue(objInput)).ToList();
var basics = props.Where(x => x.Name.StartsWith("Basic"))
.Select(x => x.GetValue(objInput)).ToList();
var rates = props.Where(x => x.Name.StartsWith("Rate"))
.Select(x => x.GetValue(objInput)).ToList();
List<RateDetail> lstDetails = new List<RateDetail>();
for (int i = 0; i < types.Count; i++)
{
lstDetails.Add(new RateDetail
{
RateType = types[i].ToString(),
Break = Convert.ToDecimal(breaks[i]),
Basic = Convert.ToDecimal(basics[i]),
Rate = Convert.ToDecimal(rates[i])
});
}

return multiple reader.cast<>

All I want to do is return multiple reader.cast<> so that i can use 2 sqlcommands.
var first =reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names = x.GetString(0),
Values = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(1)
}).ToList();
reader.NextResult();
var second=reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names2 = x.GetString(0),
Values2 = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble2 = x.GetDouble(1)
}).ToList();
All I want to do is return var first and var second. Please help :(
I'm using this Location.cs for parameters:
namespace MVCRealtime
{
public class LocationInfo
{
public string Names { get; set; }
public string Values { get; set; }
public double ValuesDouble { get; set; }
public string Names2 { get; set; }
public string Values2 { get; set; }
public double ValuesDouble2 { get; set; }
}
}
public static class ReaderHelper
{
public static IEnumerable<TElem> GetData<TElem>(this IDataReader reader, Func<IDataRecord, TElem> buildObjectDelegat)
{
while (reader.Read())
{
yield return buildObjectDelegat(reader);
}
}
}
// ...
var result = reader.GetData(x => new LocationInfo()
{
Names = x.GetString(0),
Values = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(1)
}).Take(2);
So you get 1st var in 1st element of the result and 2nd var in 2nd element.

Sorting and Updating a Generic List of Object based on a Sub Object

I have the following objects:
public class TestResult
{
public string SectionName { get; set; }
public int Score { get; set; }
public int MaxSectionScore { get; set; }
public bool IsPartialScore { get; set; }
public string Name { get; set; }
public int NumberOfAttempts { get; set; }
}
public class TestResultGroup
{
public TestResultGroup()
{
Results = new List<TestResult>();
Sections = new List<string>();
}
public List<TestResult> Results { get; set; }
public List<string> Sections { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
So, a TestResultGroup can have any number of results of type TestResult. These test results only differ by their SectionName.
I have a List<TestResultGroup> which I need to sort into descending order based on a score in the Results property, but only when Results has an item whos SectionName = "MeanScore" (if it doesnt have this section we can assume a score of -1). How would I go about ordering the list? Ideally I would also like to apply the result of this ordering to the Rank property.
Many Thanks
List<TestResultGroup> groups = ...
// group test result groups by the same score and sort
var sameScoreGroups = groups.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(gr => gr.Key);
int rank = 1;
foreach (var sameScoreGroup in sameScoreGroups)
{
foreach (var group in sameScoreGroup)
{
group.Rank = rank;
}
rank++;
}
// to obtain sorted groups:
var sortedGroups = groups.OrderByDescending(gr => gr.Rank).ToArray();
Or even write one expression with a side effect:
List<TestResultGroup> groups = ...
int rank = 1;
var sortedGroups = groups
.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(grouping => grouping.Key)
.SelectMany(grouping =>
{
int groupRank = rank++;
foreach (var group in grouping)
{
group.Rank = groupRank;
}
return grouping;
})
.ToArray(); // or ToList

Linq List within a list to string

Im trying to do a single linq statement, the following works but want to do it within a single statement.
public class ClientProducts
{
public string To { get; set; }
public string ClientFullName { get; set; }
public string ClientFirstName { get; set; }
public string ProductNames{ get; set; }
}
var list =
clients.Select(
client =>
new ClientProducts()
{
To = client.TelephoneMobile,
ClientFirstName = client.FirstName,
ClientFullName = client.FullName,
//ProductNames= client.Products.Select(p=>p.Name)<-this is what I want
}).ToList();
string productName= string.Empty;
foreach (var client in clients)
{
foreach (var p in client.Products)
{
productName+= Name+ ",";
}
}
replace
//ProductNames= client.Products.Select(p=>p.Name)
with
ProductNames = string.Join(",", client.Products.Select(p=>p.Name))

Entity to LINQ upload CSV file where single rows can have multiple values in columns

I'm currently working on parsing a csv file that was exported by another application. This application exported the data in a strange way. This export is from accoutning and it looks similar to this..
I'm trying to figure out a way to read the csv file, then split up the multiple 'All Accounts' values and 'Amt' Values so that M200 and 300.89 is another entry, M300 and 400.54 are another entry, and M400 and 100.00 are another entry. So after inserting this single row into the database, I should actually have 4 rows like so..
This is how I'm currently reading and inserting into the database.
List<RawData> data = new List<RawData>();
try
{
string text = File.ReadAllText(lblFileName.Text);
string[] lines = text.Split('\n');
int total = 0, reduced = 0;
foreach (string line in lines)
{
RawData temp = new RawData(line);
total++;
if (!(temp.FirstAccount.Length == 0 || temp.FirstAccount == "1ST-ACCT-NO"))
{
reduced++;
data.Add(temp);
}
}
}
catch (IOException ex)
{
Console.WriteLine("Unable to read file. " + ex.ToString());
MessageBox.Show(ex.ToString());
}
try
{
foreach (RawData rData in data)
{
tCarsInTransit cit = new tCarsInTransit
{
FIRST_ACCT_NO = rData.FirstAccount,
ACCOUNT_NO_DV = rData.AccountNoDv,
ACCT_NO = rData.AcctNo,
ACCT_NO_L = rData.AccNoL,
ACCT_NUM_DV = rData.AcctNumDv,
ACCT_PFX = rData.AcctPfx,
ACCT_PFX_PRT = rData.AcctPfxPrt,
ACCT_TYPE_DV = rData.AcctTypeDv,
ADV_NO = rData.AdvNo,
ALL_PRT_FLAG = rData.AllPrtFlag,
AMT = rData.Amt,
AMT_GLE = rData.AmtGle,
BASE_GLE = rData.BaseGle,
CNT_CAT = rData.CntCat,
COLD_PRT_FLAG = rData.ColdPrtFlag,
COST_DV = rData.CostDv,
COST_OVRD_FLAG_DV = rData.CostOvrdFlagDv,
CR_ACCT_DV = rData.CrAcctDv,
CR_ACCT_DV_GLE = rData.CrAcctDvGle,
CROSS_POSTING_FLAG = rData.CrossPostingFlag,
CROSS_POST_CAT = rData.CrossPostCat,
CTRL_NO = rData.CtrlNo,
CTRL_TYPE_DV = rData.CtrlTypeDv,
DESC_REQD_DV = rData.DescReqdDv,
DR_ACCT_DV = rData.DrAcctDv,
GL_DIST_ACCT_DV = rData.GLDistAcctDv,
GL_DIST_DV = rData.GLDistDv,
GRP_NO_DV = rData.GrpNoDv,
ID_PORT_DATE_TIME_FMT_CAT = rData.IdPortDateTimeFmtCat,
INACTIVITY_DV = rData.InactivityDv,
JOIN_COL = rData.JoinCol,
JRNL_DATE = rData.JrnlDate,
JRNL_PFX = rData.JrnlPfx
};
tCIT.tCarsInTransits.Add(cit);
tCIT.SaveChanges();
lblMessage.ForeColor = System.Drawing.Color.Green;
lblMessage.Text = "Finished uploading. ";
}
}
catch (DbEntityValidationException ex)
{
foreach (var eve in ex.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
I am not sure how to accomplish this. The above currently inserts the csv file into Sql Server the exact way the csv file was exported. Any ideas would greatly be appreciated! Thanks!
EDIT: Here is the RawData class.
class RawData
{
public string FirstAccount { get; set; }
public string AccountNoDv { get; set; }
public string AcctNo { get; set; }
public string AccNoL { get; set; }
public string AcctNumDv { get; set; }
public string AcctPfx { get; set; }
public string AcctPfxPrt { get; set; }
public string AcctTypeDv { get; set; }
public string AdvNo { get; set; }
public string AllPrtFlag { get; set; }
public string Amt { get; set; }
public string AmtGle { get; set; }
public string BaseGle { get; set; }
public string CntCat { get; set; }
public string ColdPrtFlag { get; set; }
public string CostDv { get; set; }
public string CostOvrdFlagDv { get; set; }
public string CrAcctDv { get; set; }
public string CrAcctDvGle { get; set; }
public string CrossPostingFlag { get; set; }
public string CrossPostCat { get; set; }
public string CtrlNo { get; set; }
public string CtrlTypeDv { get; set; }
public string DescReqdDv { get; set; }
public string DrAcctDv { get; set; }
public string GLDistAcctDv { get; set; }
public string GLDistDv { get; set; }
public string GrpNoDv { get; set; }
public string IdPortDateTimeFmtCat { get; set; }
public string InactivityDv { get; set; }
public string JoinCol { get; set; }
public string JrnlDate { get; set; }
public string JrnlPfx { get; set; }
public RawData(string csvString)
{
string[] citData = csvString.Replace(", ", "").Replace(".,", ".").Split(',');
try
{
FirstAccount = citData[0];
AccountNoDv = citData[1];
AcctNo = citData[2];
AccNoL = citData[3];
AcctNumDv = citData[4];
AcctPfx = citData[5];
AcctPfxPrt = citData[6];
AcctTypeDv = citData[7];
AdvNo = citData[8];
AllPrtFlag = citData[9];
Amt = citData[10];
AmtGle = citData[11];
BaseGle = citData[12];
CntCat = citData[13];
ColdPrtFlag = citData[14];
CostDv = citData[15];
CostOvrdFlagDv = citData[16];
CrAcctDv = citData[17];
CrAcctDvGle = citData[18];
CrossPostingFlag = citData[19];
CrossPostCat = citData[20];
CtrlNo = citData[21];
CtrlTypeDv = citData[22];
DescReqdDv = citData[23];
DrAcctDv = citData[24];
GLDistAcctDv = citData[25];
GLDistDv = citData[26];
GrpNoDv = citData[27];
IdPortDateTimeFmtCat = citData[28];
InactivityDv = citData[29];
JoinCol = citData[30];
JrnlDate = citData[31];
JrnlPfx = citData[32];
}
catch (Exception ex)
{
Console.WriteLine("Something went wrong. " + ex.ToString());
}
}
}
EDIT 2: AllAccounts in the images is acutally 'AccountNoDv' and there are actually many different fields that have multiples like 'AccountNoDv'(AllAccounts) but we might be removing those as this is not a final export. As of right now the two fields I'm worried most about are AccountNoDv and Amt.
Try something like this:
foreach (string line in lines)
{
RawData temp = new RawData(line);
var AllAccounts = temp.AccountNoDv.split(' ');
var Amts = temp.Amt.split(' ');
if (AllAccounts.Length() == Amts.Length() && Amts.Length() > 1) {
// We have multiple values!
reduced++;
for (int i = 0; i < AllAccounts.Length(); i++) {
RawData temp2 = RawDataCopy(temp); // Copy the RawData object
temp2.AccountNoDv = AllAccounts[i];
temp2.Amt = Amts[i];
total++;
data.Add(temp2);
}
}
else {
total++;
if (!(temp.FirstAccount.Length == 0 || temp.FirstAccount == "1ST-ACCT-NO"))
{
reduced++;
data.Add(temp);
}
}
}
And:
private RawData RawDataCopy(RawData copyfrom) {
// Write a function here that returns an exact copy from the one provided
// You might have to create a parameterless constructor for RawData
RawData RawDataCopy = new RawData();
RawDataCopy.FirstAccount = copyfrom.FirstAccount;
RawDataCopy.AccountNoDv = copyfrom.AccountNoDv;
RawDataCopy.AcctNo = copyfrom.AcctNo;
// . . . . . . . .
RawDataCopy.JrnlPfx = copyfrom.JrnlPfx;
return RawDataCopy;
}
Then also add a parameterless constructor to your RawData class:
public RawData()
{
}
Perhaps it would be sexier to implement the ICloneable interface and call the Clone() function instead of the RawDataCopy function, but it gets the idea across.
In Linq you can use SelectMany to increase the number of elements in a list. Here is a rough example of how this could be done. I make the assumption that the number of sub elements in AllAccounts and Amt is the same. A more robust solution would check for these issues.
So after you have loaded your data list:
var expandedData =
data.SelectMany(item =>
// split amount (will just return one item if no spaces)
item.Amt.Split(" ".ToCharArray())
// join this to the split of all accounts
.Zip(item.AllAccounts.Split(" ".ToCharArray()),
// return the joined item as a new anon object
(a,b) => new { amt=a, all=b }),
// take the original list item and the anon object and return our new item
(full,pair) => { full.Amt = pair.amt; full.AllAccounts = pair.all; return full; }));
You will now have a list of your data items with the multiple items expanded into the list.
I don't have test data to test so I might have some minor typos -- but I put in lots of comments to make the Linq as clear as possible.
Here is is simple example I wrote in LinqPad for myself to make sure I understood how SelectMany worked:
string [] list = { "a b c d","e","f g" };
var result = list.SelectMany((e) =>
e.Split(" ".ToCharArray()),
(o,item) => new { org = o, item = item}).Dump();

Categories

Resources