A VFP-Cursor in C#? - c#

I'm having a old Visual FoxPro programm, which i need to rewrite in c#.
There we used the cursors from VFP, to read .txt-files and load it into temporary cursors.
Looks for example like this in FoxPro: (mb5b is the mb5b-textfile)
SELECT werk,matnr,ALLTRIM(matnr)+ALLTRIM(werk) as matwerk,sum(zugang) as zugang,sum(abgang) as abgang INTO CURSOR mb5b_temp FROM mb5b GROUP BY werk,matnr
Those cursors dont exist in c#. (I didnt found anything like this.)
So im creating a DataTable and while reading the file I insert it into the DataTable.
DataTable dt_mb5b_temp = new DataTable();
dt_mb5b_temp.Columns.Add("matnr");
dt_mb5b_temp.Columns.Add("werk");
dt_mb5b_temp.Columns.Add("matwerk");
dt_mb5b_temp.Columns.Add("zugang");
dt_mb5b_temp.Columns.Add("abgang");
while ((mb5bline = sr_mb5b.ReadLine()) != null)
{
DataRow dr = dt_mb5b_temp.NewRow();
string[] mb5b = mb5bline.Split(new Char[] { '|' });
dr["matnr"] = mb5b[1].Trim();
dr["werk"] = mb5b[2].Trim();
dr["matwerk"] = mb5b[1].Trim() + mb5b[2].Trim();
dr["zugang"] = mb5b[6].Trim();
dr["abgang"] = mb5b[7].Trim();
}
I thought i may can work with the DataTable.Select() to use a select-statment as above, but it doesnt work ... And other solutions dont come to my mind at the moment :/
For sure i could also insert it into a DB - then use select, but i try to avoid this (Would need two extra tables, and i think those inserts and select will take a long time).
Is there any possibility to get this working ?
Thanks!
If you need anymore Informations, please tell.

look at this site. http://www.dotnetperls.com/readline
using System;
using System.Collections.Generic;
using System.IO;
class Program
{
static void Main()
{
const string f = "TextFile1.txt";
// 1
// Declare new List.
List<string> lines = new List<string>();
// 2
// Use using StreamReader for disposing.
using (StreamReader r = new StreamReader(f))
{
// 3
// Use while != null pattern for loop
string line;
while ((line = r.ReadLine()) != null)
{
// 4
// Insert logic here.
// ...
// "line" is a line in the file. Add it to our List.
lines.Add(line);
}
}
// 5
// Print out all the lines.
foreach (string s in lines)
{
Console.WriteLine(s);
}
}
}
Output
(Prints contents of TextFile1.txt)
This is a text file I created,
Just for this article.
group by ienum
class Pet
{
public string Name { get; set; }
public int Age { get; set; }
}
// Uses method-based query syntax.
public static void GroupByEx1()
{
// Create a list of pets.
List<Pet> pets =
new List<Pet>{ new Pet { Name="Barley", Age=8 },
new Pet { Name="Boots", Age=4 },
new Pet { Name="Whiskers", Age=1 },
new Pet { Name="Daisy", Age=4 } };
// Group the pets using Age as the key value
// and selecting only the pet's Name for each value.
IEnumerable<IGrouping<int, string>> query =
pets.GroupBy(pet => pet.Age, pet => pet.Name);
// Iterate over each IGrouping in the collection.
foreach (IGrouping<int, string> petGroup in query)
{
// Print the key value of the IGrouping.
Console.WriteLine(petGroup.Key);
// Iterate over each value in the
// IGrouping and print the value.
foreach (string name in petGroup)
Console.WriteLine(" {0}", name);
}
}
/*
This code produces the following output:
8
Barley
4
Boots
Daisy
1
Whiskers
*/

Related

Optimizing processing of data stored in a flat file

At my office we use an old third-party tool to handle some data processing and export work. The output of this tool is unfortunately in a really clunky format, so for us to put it into a meaningful form and work with it, we have to have an intermediate processing step between the raw export of this data and our ability to act further on it.
This problem was one that I pretty concisely solved some time ago in Python with itertools, but for reasons, I need to relocate this work into an existing C# application.
I've super-generalized and simplified the example data that I've posted here (and the corresponding code), but it's representative of the way the real data is set up. The raw data spit out by the tool looks like this, with some caveats (which I'll explain):
Zip Code: 11111
First Name: Joe
Last Name: Smith
ID: 1
Phone Number: 555-555-1111
Zip Code: 11111
First Name: John
Last Name: Doe
ID: 2
Phone Number: 555-555-1112
Zip Code: 11111
First Name: Mike
Last Name: Jones
ID: 3
Phone Number: 555-555-1113
There are no unique separators between records. They're just listed one right after the other. A valid and actionable record contains all five items ("Zip Code", "First Name", "Last Name", "ID", "Phone Number").
We only need first/last name, ID, and phone number for our purposes. Each unique record always begins with Zip Code, but thanks to some quirks in the underlying process and the third-party tool, I have some things I need to account for:
Records missing a phone number are invalid, and will show up with a value of "(n/a)" in the "Phone Number" line. We need to ignore the whole record in this case.
Records (rarely) may be missing a line (such as "Last Name") if the record was not entered correctly prior to processing. We ignore these cases, too.
If there was an error with some linked information to the underlying data, the record will contain a line beginning with "Error". Its exact position among the other items in a record varies. If a record contains an error, we ignore it.
The way I solved this in C# is to start with the first line and check to see if it begins with "Zip Code". If so, I drop into a further loop where I build a dictionary of keys and values (splitting on the first ":") until I hit the next "Zip Code" line. It then repeats and rolls through the process again while current line < (line count - 5).
private void CrappilyHandleExportLines(List<string> RawExportLines)
{
int lineNumber = 0;
while (lineNumber < (RawExportLines.Count - 5))
{
// The lineGroup dict will represent the record we're currently processing
Dictionary<string, string> lineGroup = new Dictionary<string, string>();
// If the current line begins with "Zip Code", this means we've reached another record to process
if (RawExportLines[lineNumber++].StartsWith("Zip Code"))
{
// If the line does NOT begin with "Zip Code", we assume it's another part of the record we're already
// working on.
while (!RawExportLines[lineNumber].StartsWith("Zip Code"))
{
// Append everything except "Error" lines to the record we're working on, as stored in lineGroup
if (!RawExportLines[lineNumber].StartsWith("Error")
{
string[] splitLine = RawExportLines[lineNumber].Split(new[] { ":" }, 2, StringSplitOptions.None);
lineGroup[splitLine[0].Trim()] = splitLine[1].Trim();
}
lineNumber++;
}
}
// Validate the record before continuing. verifyAllKeys is just a method that does a check of the key list
// against a list of expected keys using Except to make sure all of the items that we require are present.
if (verifyAllKeys(new List<string>(lineGroup.Keys)) || (lineGroup["Phone Number"] != "(n/a)"))
{
// The record is good! Now we can do something with it:
WorkOnProcessedRecord(lineGroup);
}
}
}
This works (from my initial testing, at least). The problem is that I really dislike this code. I know there's a better way to do it, but I'm not as strong in C# as I'd like to be so I think I'm missing out on some ways that would allow me to more elegantly and safely get the desired result.
Can anyone lend a hand to point me in the right direction as to how I can implement a better solution? Thank you!
This may help you, the idea is grouping entries based on their id by dictionary, then you can validate enitries with appropriate conditions:
static void Main(string[] args)
{
string path = #"t.txt";
var text = File.ReadAllLines(path, Encoding.UTF8);
var dict = new Dictionary<string, Dictionary<string, string>>();
var id = "";
var rows = text
.Select(l => new { prop = l.Split(':')[0], val = l.Split(':')[1].Trim() })
.ToList();
foreach (var row in rows)
{
if (row.prop == "ID")
{
id = row.val;
}
else if (dict.ContainsKey(id))
{
dict[id].Add(row.prop, row.val);
}
else
{
dict[id] = new Dictionary<string, string>();
dict[id].Add(row.prop, row.val);
}
}
//get valid entries
var validEntries = dict.Where(e =>e.Value.Keys.Intersect(new List<string> { "Zip Code", "First Name", "Last Name", "Phone Number" }).Count()==4 && e.Value["Phone Number"] != "(n/a)").ToDictionary(x=>x.Key, x => x.Value);
}
In case ID is related to previous properties and emerges after them you can use below code as If block :
if (row.prop == "ID")
{
var values=dict[id];
dict.Remove(id);
dict.Add(row.val,values);
id = "";
}
I would try to solve the problem in a bit more of an object oriented manner using a factory-ish pattern.
//Define a class to hold all people we get, which might be empty or have problems in them.
public class PersonText
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string ID { get; set; }
public string ZipCode { get; set; }
public bool Error { get; set; }
public bool Anything { get; set; }
}
//A class to hold a key ("First Name"), and a way to set the respective item on the PersonText class correctly.
public class PersonItemGetSets
{
public string Key { get; }
public Func<PersonText, string> Getter { get; }
public Action<PersonText, string> Setter { get; }
public PersonItemGetSets(string key, Action<PersonText, string> setter, Func<PersonText, string> getter)
{
Getter = getter;
Key = key;
Setter = setter;
}
}
//This will get people from the lines of text
public static IEnumerable<PersonText> GetPeople(IEnumerable<string> lines)
{
var itemGetSets = new List<PersonItemGetSets>()
{
new PersonItemGetSets("First Name", (p, s) => p.FirstName = s, p => p.FirstName),
new PersonItemGetSets("Last Name", (p, s) => p.LastName = s, p => p.LastName),
new PersonItemGetSets("Phone Number", (p, s) => p.PhoneNumber = s, p => p.PhoneNumber),
new PersonItemGetSets("ID", (p, s) => p.ID = s, p => p.ID),
new PersonItemGetSets("Zip Code", (p, s) => p.ZipCode = s, p => p.ZipCode),
};
foreach (var person in GetRawPeople(lines, itemGetSets, "Error"))
{
if (IsValidPerson(person, itemGetSets))
yield return person;
}
}
//Used to determine if a PersonText is valid and if it is worth processing.
private static bool IsValidPerson(PersonText p, IReadOnlyList<PersonItemGetSets> itemGetSets)
{
if (itemGetSets.Any(x => x.Getter(p) == null))
return false;
if (p.Error)
return false;
if (!p.Anything)
return false;
if (p.PhoneNumber.Length != 12) // "555-555-5555".Length = 12
return false;
return true;
}
//Read through each line, and return all potential people, but don't validate whether they're correct at this time.
private static IEnumerable<PersonText> GetRawPeople(IEnumerable<string> lines, IReadOnlyList<PersonItemGetSets> itemGetSets, string errorToken)
{
var person = new PersonText();
foreach (var line in lines)
{
var parts = line.Split(':');
bool valid = false;
if (parts.Length == 2)
{
var left = parts[0];
var right = parts[1].Trim();
foreach (var igs in itemGetSets)
{
if (left.Equals(igs.Key, StringComparison.OrdinalIgnoreCase))
{
valid = true;
person.Anything = true;
if (igs.Getter(person) != null)
{
yield return person;
person = new PersonText();
}
igs.Setter(person, right);
}
}
}
else if (parts.Length == 1)
{
if (parts[0].Trim().Equals(errorToken, StringComparison.OrdinalIgnoreCase))
{
person.Error = true;
}
}
if (!valid)
{
if (person.Anything)
{
yield return person;
person = new PersonText();
}
continue;
}
}
if (person.Anything)
yield return person;
}
Have a look at the code working here: https://dotnetfiddle.net/xVnATX

Is there a way to include null values in a list in C#?

I have a text file filled with several lines of data, and I would like to split it into 5 different elements like so..
I am successfully reading in the data and putting it into an array. Now I would like to split each part of the text up into different lists so I can compare the data against one another.
I have currently managed to read in the first 4 elements of each line into their relevant lists but the 5th one is throwing me the error "System.IndexOutOfRangeException" which I can only assume is because the first line it reads in has no value for the 5th element?
So my question is, is there a way to populate null values when writing them to a number of lists?
I've tried manually assigning the size of the array and lists but I still get the same error.
Here is my code:
class Program
{
static void Main(string[] args)
{
// Reading in file containing data from BT Code Evaluation sheet (for testing purposes).
// Each line gets stored into a string array, each element is one line of the data.txt file.
//String[] lines = System.IO.File.ReadAllLines(#"C:\Users\Ad\Desktop\data.txt");
String[] lines = new String[5] {"monitorTime", "localTime", "actor", "action", "actor2"};
lines = System.IO.File.ReadAllLines(#"C:\Users\Ad\Desktop\data.txt");
char delimiter = ' ';
List<String> monitorTime = new List<String>();
List<String> localTime = new List<String>();
List<String> actor = new List<String>();
List<String> action = new List<String>();
List<String> actor2 = new List<String>();
// Foreach loop displays the lines of text in the data file.
foreach (String line in lines)
{
// Writes the data to the console.
Console.WriteLine(line);
String[] data = new String[5] { "monitorTime", "localTime", "actor", "action", "actor2" };
data = line.Split(delimiter);
monitorTime.Add(data[0]);
localTime.Add(data[1]);
actor.Add(data[2]);
action.Add(data[3]);
actor2.Add(data[4]);
}
foreach (String time in monitorTime)
{
Console.WriteLine(time);
}
foreach (String time in localTime)
{
Console.WriteLine(time);
}
foreach (String name in actor)
{
Console.WriteLine(name);
}
foreach (String actions in action)
{
Console.WriteLine(actions);
}
foreach (String name in actor2)
{
if (name != null)
{
Console.WriteLine("UNKNOWN");
}
else
{
Console.WriteLine(actor2);
}
}
// Creates an empty line between the data and the following text.
Console.WriteLine("");
// Displays message in console.
Console.WriteLine("Press any key to analyse data and create report...");
Console.ReadKey();
}
}
You need to check the bounds of you array before you try to add. If their aren't enough items you can add null instead.
For example:
actor2.Add(data.length > 4 ? data[4] : null)
(Note: You could do the same type of check on the other items as well, unless you are positive that the last item is the only one that might be null)
This is using the ternary operator, but you could also use a simple if/else, but it'll be more verbose. It's equivalent to:
if (data.length > 4)
{
actor2.Add(data[4]);
}
else
{
actor2.Add(null);
}
This along with Console.WriteLine(name); instead of Console.WriteLine(actor2); should fix you immediate problem.
However, a much better design here would be to have a single list of objects with MonitorTime, LocalTime, Actor, Action and Actor2 properties. That way you don't ever have to worry that the 5 parallel arrays might get out of sync.
For example, create a class like this:
public class DataItem
{
public string MonitorTime { get; set; }
public string LocalTime { get; set; }
public string Actor { get; set; }
public string Action { get; set; }
public string Actor2 { get; set; }
}
Then instead of your 5 List<String>, you have one List<DataItem>:
List<DataItem> dataList = new List<DataItem>();
Then in your loop to populate it you'd do something like:
data = line.Split(delimiter);
dataList.Add(new DataItem()
{
MonitorTime = data[0],
LocalTime = data[1],
Actor = data[2],
Action = data[3],
Actor2 = data.length > 4 ? data[4] : null
});
Then you can access them later with something like:
foreach (var item in dataList)
{
Console.WriteLine(item.MonitorTime);
//...
}
In your for each you should be checking to see if the index exists before populating the object.
foreach (String line in lines)
{
// Writes the data to the console.
Console.WriteLine(line);
String[] data = new String[5] { "monitorTime", "localTime", "actor", "action", "actor2" };
data = line.Split(delimiter);
monitorTime.Add(data[0]);
localTime.Add(data[1]);
actor.Add(data[2]);
action.Add(data[3]);
if (data.Length > 4) {
actor2.Add(data[4]);
}
}
There's better ways to do this but this is a simple solution for now.

how to remove duplicate data for group query in Linq

I'm trying to find a distinct list of filenames related to each bugid, and I used linq to group all filenames related to each bug id. I don't know how I can remove duplicate filenames related to each bugid,in file ouput I have multiple rows like this:
bugid filename1 filename2 filename3 filename4 .............
there are multiple rows with the same bugid and also there duplicate filenames for each bug id,
this is my code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace finalgroupquery
{
class MainClass
{
public static void Main (string[] args)
{
List <bug> list2=new List <bug> ();
using(System.IO.StreamReader reader1= new System.IO.StreamReader( #"/home/output"))
using (System.IO.StreamWriter file = new System.IO.StreamWriter( #"/home/output1"))
{string line1;
while ((line1=reader1.ReadLine())!=null)
{ string[] items1=line1.Split('\t');
bug bg=new bug();
bg.bugid=items1[0];
for (int i=1; i<=items1.Length -1;i++)
{ bg.list1.Add(items1[i]);}
list2.Add(bg);
}
var bugquery= from c in list2 group c by c.bugid into x select
new Container { BugID = x.Key, Grouped = x };
foreach (Container con in bugquery)
{
StringBuilder files = new StringBuilder();
files.Append(con.BugID);
files.Append("\t");
foreach(var x in con.Grouped)
{
files.Append(string.Join("\t", x.list1.ToArray()));
}
file.WriteLine(files.ToString()); }
}
}
}
public class Container
{
public string BugID {get;set;}
public IGrouping<string, bug> Grouped {get;set;}
}
public class bug
{
public List<string> list1{get; set;}
public string bugid{get; set;}
public bug()
{
list1=new List<string>();
}
}
}
}
From your description it sounds like you want to do this:
List <bug> bugs = new List<bug>();
var lines = System.IO.File.ReadLines(#"/home/bugs");
foreach (var line in lines) {
string[] items = line.Split('\t');
bug bg=new bug();
bg.bugid = items[0];
bg.list1 = items.Skip(1).OrderBy(f => f).Distinct().ToList();
bugs.Add(bg);
}
This will produce a list of objects, where each object has a unique list of filenames.
Try to use this code :
var bugquery = from c in list2
group c by c.bugid into x
select new bug { bugid = x.Key, list1 = x.SelectMany(l => l.list1).Distinct().ToList() };
foreach (bug bug in bugquery)
{
StringBuilder files = new StringBuilder();
files.Append(bug.bugid);
files.Append("\t");
files.Append(string.Join("\t", bug.list1.ToArray()));
file.WriteLine(files.ToString());
}
Thanks to the combination of SelectMany and Distinct Linq operators, you can flatten the filename list and delete duplicates in a single line.
SelectMany (from msdn):
Projects each element of a sequence to an IEnumerable and flattens
the resulting sequences into one sequence.
Distinct (from msdn):
Returns distinct elements from a sequence.
It also means that your Container class is no longer needed as there's no need to iterate through the IGrouping<string, bug> collection anymore (here list1 contains all the bug related filenames without duplicates).
Edit
As you may have some blank lines and/or empty strings after reading and parsing your file, you could use this code to get rid of them :
using (System.IO.StreamReader reader1 = new System.IO.StreamReader(#"/home/sunshine40270/mine/projects/interaction2/fasil-data/common history/outputpure"))
{
string line1;
while ((line1 = reader1.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line1))
{
string[] items1 = line1.Split(new [] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
bug bg = new bug();
bg.bugid = items1[0];
for (int i = 1; i <= items1.Length - 1; i++)
{
bg.list1.Add(items1[i]);
}
list2.Add(bg);
}
}
}
You'll notice :
New lines stored in line1 are checked for emptyness as soon as they are retrieved from your stream (with !string.IsNullOrWhiteSpace(line1))
To omit empty substrings from the return value of the string.Split method, you can use the StringSplitOptions.RemoveEmptyEntries parameter.
Hope this helps.

Creating a two-dimensional array

I am trying to create a two dimensional array and I am getting so confused. I was told by a coworker that I need to create a dictionary within a dictionary for the array list but he couldn't stick around to help me.
I have been able to create the first array that lists the the programs like this
+ project 1
+ project 2
+ project 3
+ project 4
The code that accomplishes this task is below-
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
//select x.PG.Distinct().ToArray();
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
So that takes care of my vertical array and now I need to add my horizontal array so that I can see the total amount spent in each accounting period. So the final output would look like this but without the dashes of course.
+ program 1-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 2-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 3-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 4-------100---200---300---400---500---600---700---800---900---1000---1100---1200
I have tried to use a foreach to cycle through the accounting periods but it doesn't work. I think I might be on the right track and I was hoping SO could provide some guidance or at the very least a tutorial for me to follow. I have posted the code that I written so far on the second array below. I am using C# and MVC 3. You might notice that their is no dictionary within a dictionary. If my coworker is correct how would I do something like that, I took a look at this question using dictionary as a key in other dictionary but I don't understand how I would use it in this situation.
Dictionary<string, double[]> MonthRow = new Dictionary<string, double[]>();
double[] PGContent = new double[12];
string lastPG = null;
foreach (var item in PGRow)
{
if (lastPG != item.PG)
{
PGContent = new double[12];
}
var MonthList = from x in db.Month_Web
where x.PG == PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var P in MonthList)
{
int accounting_period = int.Parse(P.accounting_period) - 1;
PAContent[accounting_period] = (double)P.amount;
MonthRow[item.PG] = PGContent;
lastPG = item.PG;
}
I hope I have clearly explained my issue, please feel free to ask for any clarification needed as I need to solve this problem and will be checking back often. Thanks for your help!
hope this helps.
// sample data
var data = new Dictionary<string, List<int>>();
data.Add("program-1", new List<int>() { 100, 110, 130 });
data.Add("program-2", new List<int>() { 200, 210, 230 });
data.Add("brogram-3", new List<int>() { 300, 310, 330 });
// query data
var newData = (from x in data
where x.Key.Contains("pro")
select x).ToDictionary(v => v.Key, v=>v.Value);
// display selected data
foreach (var kv in newData)
{
Console.Write(kv.Key);
foreach (var val in kv.Value)
{
Console.Write(" ");
Console.Write(val.ToString());
}
Console.WriteLine();
}
output is:
program-1 100 110 130
program-2 200 210 230
Don't try to use anonymous types or LINQ projection to create new data types, especially if you're a beginner, you will just get confused. If you want a specialized data type, define one; e.g.:
public class Account
{
public string Name { get; private set; }
public decimal[] MonthAmount { get; private set; }
readonly int maxMonths = 12;
public Account(string name, ICollection<decimal> monthAmounts)
{
if (name == null)
throw new ArgumentNullException("name");
if (monthAmounts == null)
throw new ArgumentNullException("monthAmounts");
if (monthAmounts.Count > maxMonths)
throw new ArgumentOutOfRangeException(string.Format(" monthAmounts must be <= {0}", maxMonths));
this.Name = name;
this.MonthAmount = new decimal[maxMonths];
int i = 0;
foreach (decimal d in monthAmounts)
{
this.MonthAmount[i] = d;
i++;
}
}
}
Use instances of this type directly, you do not have to convert them to arrays, dictionaries, lists, or anything else:
var accountPeriods = new List<Account>();
accountPeriods.Add(new Account("program-1", new decimal[] { 1, 2, 3, 4 }));
You can use LINQ or whatever to query or alter instances of your new type:
foreach (Account a in accountPeriods)
foreach (decimal d in a.MonthAmount)
DoSomethingWith(d);
That should be enough to get you started.
I want to thank #Ray Cheng and #Dour High Arch for their help but I have figured out another way to accomplish this task and I wanted to post my code so that the next person that is having the same trouble can figure out their problem faster.
Above I split my code into more managable sections to explain my problem as clearly as I could and the code below has all those parts combined so you can see the big picture. This code returns an array that contains the program and the amounts for every month.
public virtual ActionResult getAjaxPGs(string SP = null)
{
if (SP != null)
{
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
float[] PGContent = new float[12];
Dictionary<string,float[]> MonthRow = new Dictionary<string, float[]>();
foreach (var item in PGRow)
{
PGContent = new float[12];
var MonthList = from x in db.month_Web
where x.PG == item.PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var mon in MonthList)
{
int accounting_period = int.Parse(mon.accounting_period) - 1;
PGContent[accounting_period] = (float)mon.amount/1000000;
}
MonthRow[item.PG] = PGContent;
}
return Json(MonthRow, JsonRequestBehavior.AllowGet);
}
return View();
}
This code worked great for me since I am pulling from a Linq to SQL query instead of adding data directly into the code. My problems stemmed from mainly putting the data pulls outside of the foreach loops so it only pulled 1 piece of data from the SQL instead of all twelve months. I hope this helps some one else who is trying to pull data in from SQL data sources into multidimensional arrays.

C# Linq question

I have a text file in which I am storing entries for an address book.
The layout is like so:
Name:
Contact:
Product:
Quantity:
I have written some linq code to grab the name plus the next four lines, for a search by name feature.
I also want to be able to search by contact.
The challenge is to match the contact info, grab the next 3 lines, and also grab the line prior to the match.
That way if Search By Contact is used, the full list of info will be returned.
private void buttonSearch_Click(object sender, EventArgs e)
{
string[] lines = File.ReadAllLines("C:/AddressBook/Customers.txt");
string name = textBoxSearchName.Text;
string contact = textBoxContact.Text;
if (name == "" && contact == "")
{
return;
}
var byName = from line in lines
where line.Contains(name)
select lines.SkipWhile(f => f != line).Take(4);
//var byContact = from line in lines
// where line.Contains(name)
// select lines.SkipWhile(f => f != name).Take(4);
if (name != "")
{
foreach (var item in byName)
foreach (var line in item) { listBox2.Items.Add(line); }
listBox2.Items.Add("");
}
//if (contact != "")
//{
// foreach (var item in byContact)
// foreach (var line in item) { listBox2.Items.Add(line); }
//listBox2.Items.Add("");
}
}
Firstly i would recommend changing your data storage approach if you can.
Secondly i would recommend reading the file into an object, something like this:
public class Contact
{
public string Name {get; set;}
public string Contact {get; set;}
public string Product {get; set;}
public int Quantity {get; set;}
}
...
public IEnumerable<Contact> GetContacts()
{
//make this read line by line if it is big!
string[] lines = File.ReadAllLines("C:/AddressBook/Customers.txt");
for (int i=0;i<lines.length;i += 4)
{
//add error handling/validation!
yield return new Contact()
{
Name = lines[i],
Contact = lines[i+1],
Product = lines[i+2],
Quantity = int.Parse(lines[i+3]
};
}
}
private void buttonSearch_Click(object sender, EventArgs e)
{
...
var results = from c in GetContacts()
where c.Name == name ||
c.Contact == contact
select c;
...
}
See if this will work
var contactLinesList = lines.Where(l => l.Contains(name))
.Select((l, i) => lines.Skip(i - 1).Take(4)).ToList();
contactLinesList.ForEach(cl => listBox2.Items.Add(cl));
This is not the smallest code in earth but it shows how to do a couple of things. Although I don't recommend using it, because it is quite complex to understand. This is to be considered as a hobbyist, just learning code!!! I suggest you load the file in a well known structure, and do Linq on that... anyway... this is a C# Console Application that does what you proposed using Linq syntax, and one extension method:
using System;
using System.Collections.Generic;
using System.Linq;
namespace stackoverflow.com_questions_5826306_c_linq_question
{
public class Program
{
public static void Main()
{
string fileData = #"
Name: Name-1
Contact: Xpto
Product: Abc
Quantity: 12
Name: Name-2
Product: Xyz
Contact: Acme
Quantity: 16
Name: Name-3
Product: aammndh
Contact: YKAHHYTE
Quantity: 2
";
string[] lines = fileData.Replace("\r\n", "\n").Split('\n');
var result = Find(lines, "contact", "acme");
foreach (var item in result)
Console.WriteLine(item);
Console.WriteLine("");
Console.WriteLine("Press any key");
Console.ReadKey();
}
private static string[] Find(string[] lines, string searchField, string searchValue)
{
var result = from h4 in
from g4 in
from i in (0).To(lines.Length)
select ((from l in lines select l).Skip(i).Take(4))
where !g4.Contains("")
select g4
where h4.Any(
x => x.Split(new char[] { ':' }, 2)[0].Equals(searchField, StringComparison.OrdinalIgnoreCase)
&& x.Split(new char[] { ':' }, 2)[1].Trim().Equals(searchValue, StringComparison.OrdinalIgnoreCase))
select h4;
var list = result.FirstOrDefault();
return list.ToArray();
}
}
public static class NumberExtensions
{
public static IEnumerable<int> To(this int start, int end)
{
for (int it = start; it < end; it++)
yield return it;
}
}
}
If your text file is small enough, I'd recommend using regular expressions instead. This is exactly the sort of thing it's designed to do. Off the top of my head, the expression will look something like this:
(?im)^Name:(.*?)$ ^Contact:search_term$^Product:(.*?)$^Quantity:(.*?)$

Categories