You guys are ruthless, makes a new programmer like me feel real welcome. :)
Alright let me try this one more time if I can correctly explain my situation. Like one of answer below I have a string that contains the following information. This was created using a while loop where each line ends with an Environment.Newline (There is no mistake in the first line, there is actually a blank line).
var s = #"
ABC-123, 80000, 1400
ABC-123, 70000, 1250
ABC-123, 65000, 1200
BCD-234, 90000, 1300
BCD-234, 95000, 1100
XYZ-111, 24000, 1000
XYZ-111, 24000, 1000"
I originally asked if there is a way to group by the first column ie. all ABC-123 are grouped together, the second is summed and the third column is averaged. Please ignore the sum and average, I just need to understand how to group first.
Here's where I get confused, by using this statement for one of the answers below:
var ss = s.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.GroupBy(y => y[0]);
I understand what the answer is trying to do but I need help writing the result back into a string (maybe that's not the right choice, I don't know, always open to suggestions)so that I can use StreamWriter to save the result as a csv.
I've tried to understand IEnumerables but all the videos/websites just confuse the hell out of me. I've also tried outputting the results of ss so that maybe if I got a visual representation, then I could rewrite it but when I do I get the following results:
Console.WriteLine(ss);
Console.Read();
System.Linq.GroupedEnumerable3[<>f__AnonymousType01[System.Char],System.Char,<
f__AnonymousType0`1[System.Char]]
The output I would want would be a string that looked like this.
output = "ABC-123, 215000, 1283
BCD-234, 185000, 1200
XYZ-111, 48000, 1000"
Assuming all your inputdata is one long string:
var s = #"ABC-123, 80000, 1400
ABC-123, 70000, 1250
ABC-123, 65000, 1200
BCD-234, 90000, 1300
BCD-234, 95000, 1100
XYZ-111, 24000, 1000
XYZ-111, 24000, 1000";
var ss = s.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) //split by newlines
.Select(x => x.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) //Split each line by ,
.GroupBy(y => y[0]); //group by first element of array
If it is an array of string already, you can ignore the first split by newlines
EDIT: Well, we cannot explain the theory behind IEnumerable, LINQ and grouping here. There are plenty of excellent tutorials on that. Maybe you should learn some basics first before jumping into such stuff.
But for your particular problem this should do it:
var lines = ss.Select(z => new {cKey = z.Key,
c2sum = z.Select(a=> Convert.ToInt32(a[1])).Sum(),
c3avg = z.Select(a=> Convert.ToInt32(a[2])).Average()});
foreach (var l in lines)
Console.WriteLine("{0}, {1}, {2}", l.cKey, l.c2sum, l.c3avg); //or whatever stream you want to write to
Assuming that your output is an array of strings, one way would be to split the strings by commas and group by the first result:
list.Select(s => s.Split(','))
.GroupBy(a => a[0])
Note that the output will be an IEnumerable<string[]> - if you want the original string just keep it in the original select:
list.Select(s => new {S = s, Parts = s.Split(',')})
.GroupBy(a => a.Parts[0])
.Select(g => new {Key = g.Key, lines = g.Select(a => a.S) } );
Related
I need to order items in an IEnumerable based on a string property that contains leading 0s. The items need to be ordered by 000. then 00. and then it goes to 0.
A simplified example:
Unordered - ["0.4","0.1", "0.3", "00.5", "000.1", "1.2", "00.2", "2.1"]
Ordered - ["000.1", "00.2", "00.5", "0.1", "0.3", "0.4", "1.2", "2.1"]
The current implementation is:
items.OrderBy(x => x.age);
which orders the items as ["0.1", "0.3", "0.4", "00.2", "00.5", "000.1", "1.2", "2.1"]
Since these are strings, I thought I might try to convert them to decimals, but I'm still not getting the order I need.
items.OrderBy(x => decimal.Parse(x.age))
[ 0.1, 000.1, 00.2, 0.3, 0.4, 00.5, 1.2, 2.1]
Any advice on how to get the order I need would be greatly appreciated!
I suggest comparing strings; the difficulty is that '.' < '0'; to have a required order we can try changing '.' into some other delimiter that's lexicographically bigger then any digit. Let it be a letter, say 'x':
var result = items.OrderBy(item => item.Replace('.', 'x'));
Demo:
string[] items = new string[] {
"0.4","0.1", "0.3", "00.5", "000.1", "1.2", "00.2", "2.1"
};
var result = items
.OrderBy(item => item.Replace('.', 'x'));
Console.Write(string.Join(", ", result));
Outcome:
000.1, 00.2, 00.5, 0.1, 0.3, 0.4, 1.2, 2.1
Welcome to Stack Overflow,
I think this snippet may help you:
items.OrderBy(x => x.age.Split('.')[0].Length).ThenBy(x => decimal.Parse(x.age));
With this snippet, we split the value on the decimal dot, and then OrderBy the count of 0's on the first part, and if we have two values with the same count of leading zeros, we compare them as ordinary decimals.
Try the following:
items.OrderBy(x => x.Split(".")[0]).ThenBy(x => x.Split(".")[1]);
I am sorry if i worded to question wrongly and caused confusion. But here is what I want to do. I want the program to read the txt file and then arrange the data into like a table format?
The txt file can look something like this:
fullname1|ID1|age1|mark1
fullname2|ID2|age2|mark2
fullname3|ID3|age3|mark3
I have looked up several ways to do it but none of them worked for me, and I am self learning C# but my English is limited so it is hard for me to look for deeper solution.
Because i'm bored
Given
fullname1|ID1|age1|mark1546
sdfdsf|ID2|age56|mark2
gfhxcxvxcvxc|ID3|age3|mark3
Code
// read lines
var lines = File.ReadAllLines(#"D:\Data.txt")
.Select(x => x.Split('|'))
.ToArray();
// calculate column widths
var widths = Enumerable.Range(0, lines[0].Length)
.Select(x => lines.Max(y => y[x].Length))
.ToArray();
// Write the lines with padding each column with its max width
foreach (var line in lines)
Console.WriteLine(string.Join(", ", line.Select((x,i) => x.PadRight(widths[i], ' '))));
Output
fullname1 , ID1, age1 , mark1546
sdfdsf , ID2, age56, mark2
gfhxcxvxcvxc, ID3, age3 , mark3
DISCLAIMER : obviously this makes assumptions, therefor i am not responsible for the people you potentialy maim or otherwise harm with this code
I have tried several methods (by position, by white space, regex) but cannot figure how to best parse the following lines as a table. For e.g. let's say the two lines I want to parse are:
Bonds Bid Offer (mm) (mm) Chng
STACR 2015-HQA1 M1 125 120 5 x 1.5 0
STACR 2015-HQA12 2M2 265 5 x -2
I want that it should parse as follows for [BondName] [Bid] [Offer]:
[STACR 2015-HQA1 M1] [125] [120]
[STACR 2015-HQA12 2M2] [265] [null]
Notice the null which is an actual value and also the spaces should be retained in the bond name. FYI, the number of spaces in the Bond Name will be 2 as in the above examples.
Edit: Since many of you have asked for code here it is. The spaces between the points can range from 1-5 so I cannot reply on spaces (it was straightforward then).
string bondName = quoteLine.Substring(0, 19);
string bid = quoteLine.Substring(19, 5).Trim();
string offer = quoteLine.Substring(24, 6).Trim();
The only way I can see this working is that:
1st data point is STACR (Type)
2nd data point is the year and Series
(e.g. 2015-HQA1)
3rd data point is Tranche (M1)
4th data point is bid
(e.g. 125 ** bid is always available **)
5th data point is offer (e.g. 120 but can be blank
or whitespace which introduces complexity)
With the current set of requirements, I'm assuming the following
1. String starts with 3 part bond name
2. Followed by bid
3. Followed by offer (optional)
4. After that, we'll have something like ... x ... ... (we'll use x as reference)
Given they are valid, you can use the following code
var str = "STACR 2015-HQA1 M1 125 120 5 x 1.5 0"; //your data
var parts = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
//we'll use this pattern : <3 part bond name> <bid> <offer/null> <something x ....>
var xIsAt = parts.IndexOf("x"); //we'll use x as reference
if (xIsAt > 2) //first three are BondName
parts.RemoveRange(xIsAt - 1, parts.Count - xIsAt + 1); //remove "5 x 1.5 ..."
var bond = string.Join(" ", parts.Take(3)); //first 3 parts are bond
var bid = parts.Count > 3 ? parts.ElementAt(3) : null; //4th is bid
var offer = parts.Count > 4 ? parts.ElementAt(4) : null; //5th is offer
[EDIT]
I did not account for the blank 'Offer' so this method will fail on a blank 'Offer'. Looks like someone already has a working answer, but i'll leave the linq example for anyone that finds it useful.
[END EDIT]
Linq based option.
Split the string by spaces, and remove empty spaces. Then reverse the order so you can start from the back and work your way forward. The data appears more normalized at the end of the string.
For each successive part of the line, you skip the previous options and only take what you need. For the last part which is the long string, you skip what you don't need, then reverse the order back to normal, and join the segments together with spaces.
string test = "STACR 2015-HQA1 M1 125 120 5 x 1.5 0";
var split_string_remove_empty = test.Split(new char[]{ ' ' }, StringSplitOptions.RemoveEmptyEntries).Reverse();
var change = split_string_remove_empty.Take(1)
.SingleOrDefault();
var mm2 = split_string_remove_empty.Skip(1)
.Take(1)
.SingleOrDefault();
var mm3 = split_string_remove_empty.Skip(3)
.Take(1)
.SingleOrDefault();
var offer = split_string_remove_empty.Skip(4)
.Take(1)
.SingleOrDefault();
var bid = split_string_remove_empty.Skip(5)
.Take(1)
.SingleOrDefault();
var bonds = string.Join(" ", split_string_remove_empty.Skip(6)
.Reverse());
Output:
I have a text file which contains lot of data each on new line
but i want to extract the lines, which start with the values:
coordinates=(111,222,333)
There are several instances of this line and i would actually want to extract the part "111,222,333"
how can i do this?
Something like
var result = File.ReadAllLines(#"C:\test.txt")
.Where(p => p.StartsWith("coordinates=("))
.Select(p => p.Substring(13, p.IndexOf(')') - 13));
The first line is quite clear, the second line filters for only the lines that starts with coordinates=(, the third line extract the substring (13 is the length of coordinates=()
result is an IEnumerable<string>. You can convert it to an array with result.ToArray()
var text = File.ReadAllText(path);
var result = Regex.Matches(text, #"coordinates=\((\d+),(\d+),(\d+)\)")
.Cast<Match>()
.Select(x => new
{
X = x.Groups[1].Value,
Y = x.Groups[2].Value,
Z = x.Groups[3].Value
})
.ToArray();
Given a data file delimited by space,
10 10 10 10 222 331
2 3 3 4 45
4 2 2 4
How to read this file and load into an Array
Thank you
var fileContent = File.ReadAllText(fileName);
var array = fileContent.Split((string[])null, StringSplitOptions.RemoveEmptyEntries);
if you have numbers only and need a list of int as a result, you can do this:
var numbers = array.Select(arg => int.Parse(arg)).ToList();
It depends on the kind of array you want. If you want to flatten everything into a single-dimensional array, go with Alex Aza's answer, otherwise, if you want a 2-dimensional array that maps to the lines and elements within the text file:
var array = File.ReadAllLines(filename)
.Select(line => line.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.Where(line => !string.IsNullOrWhiteSpace(line)) // Use this to filter blank lines.
.Select(int.Parse) // Assuming you want an int array.
.ToArray();
Be aware that there is no error handling, so if parsing fails, the above code will throw an exception.
You will be interested in StreamReader.ReadLine() and String.Split()
I couldn't get Quick Joe Smith's answer to work, so I modified it. I put the modified code into a static method within a "FileReader" class:
public static double[][] readWhitespaceDelimitedDoubles(string[] input)
{
double[][] array = input.Where(line => !String.IsNullOrWhiteSpace(line)) // Use this to filter blank lines.
.Select(line => line.Split((string[])null, StringSplitOptions.RemoveEmptyEntries))
.Select(line => line.Select(element => double.Parse(element)))
.Select(line => line.ToArray())
.ToArray();
return array;
}
For my application, I was parsing for double as opposed to int. To call the code, try using something like this:
string[] fileContents = System.IO.File.ReadAllLines(openFileDialog1.FileName);
double[][] fileContentsArray = FileReader.readWhitespaceDelimitedDoubles(fileContents);
Console.WriteLine("Number of Rows: {0,3}", fileContentsArray.Length);
Console.WriteLine("Number of Cols: {0,3}", fileContentsArray[0].Length);