How do I get values from a stringbuilder with multiple delimiters? - c#

I have a stringbuilder output as
(AnimalAge;Dog:20,Cat:10,Rat:5#\r\nAnimalType;Whale:Mammal,Crocodile:Reptile#\r\n)
I want to have AnimalAge as different entity and AnimalType as a different output.
How do i separate these values so that i can populate this in different textboxes\labels?

I didn't optimize the code, but it should look like
var text = new StringBuilder("AnimalAge;Dog:20,Cat:10,Rat:5#\r\nAnimalType;Whale:Mammal,Crocodile:Reptile#\r\n");
var cat = text.ToString().Split(new[] { "#\r\n" }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(new[] { ";" }, StringSplitOptions.None))
.Select(x => new
{
Category = x[0],
Values = x[1].Split(new[] { "," }, StringSplitOptions.None)
})
.Select(x => new
{
Category = x.Category,
Values = x.Values.ToDictionary(y => y.Split(new[] { ":" }, StringSplitOptions.None)[0], y => y.Split(new[] { ":" }, StringSplitOptions.None)[1])
})
.ToList();
In the end you will get a list of Category/Values object, where Values is a Dictionary

Related

c# read lines and store values in dictionaries

I want to read a csv file and store the values in a correct way in dictionaries.
using (var reader = new StreamReader(#"CSV_testdaten.csv"))
{
while (!reader.EndOfStream)
{
string new_line;
while ((new_line = reader.ReadLine()) != null)
{
var values = new_line.Split(",");
g.add_vertex(values[0], new Dictionary<string, int>() { { values[1], Int32.Parse(values[2]) } });
}
}
}
the add_vertex function looks like this:
Dictionary<string, Dictionary<string, int>> vertices = new Dictionary<string, Dictionary<string, int>>();
public void add_vertex(string name, Dictionary<string, int> edges)
{
vertices[name] = edges;
}
The csv file looks like this:
there are multiple lines with the same values[0] (e.g. values[0] is "0") and instead of overwriting the existing dictionary, it should be added to the dictionary which already exists with values[0] = 0. like this:
g.add_vertex("0", new Dictionary<string, int>() { { "1", 731 } ,
{ "2", 1623 } , { "3" , 1813 } , { "4" , 2286 } , { "5" , 2358 } ,
{ "6" , 1 } , ... });
I want to add all values which have the same ID (in the first column of the csv file) to one dictionary with this ID. But I'm not sure how to do this. Can anybody help?
When we have complex data and we want to query them, Linq can be very helpful:
var records = File
.ReadLines(#"CSV_testdaten.csv")
.Where(line => !string.IsNullOrWhiteSpace(line)) // to be on the safe side
.Select(line => line.Split(','))
.Select(items => new {
vertex = items[0],
key = items[1],
value = int.Parse(items[2])
})
.GroupBy(item => item.vertex)
.Select(chunk => new {
vertex = chunk.Key,
dict = chunk.ToDictionary(item => item.key, item => item.value)
});
foreach (var record in records)
g.add_vertex(record.vertex, record.dict);
Does this work for you?
vertices =
File
.ReadLines(#"CSV_testdaten.csv")
.Select(x => x.Split(','))
.Select(x => new { vertex = x[0], name = x[1], value = int.Parse(x[2]) })
.GroupBy(x => x.vertex)
.ToDictionary(x => x.Key, x => x.ToDictionary(y => y.name, y => y.value));
You can split your code in two parts. First will read csv lines:
public static IEnumerable<(string, string, string)> ReadCsvLines()
{
using (var reader = new StreamReader(#"CSV_testdaten.csv"))
{
while (!reader.EndOfStream)
{
string newLine;
while ((newLine = reader.ReadLine()) != null)
{
var values = newLine.Split(',');
yield return (values[0], values[1], values[2]);
}
}
}
}
and second will add those lines to dictionary:
var result = ReadCsvLines()
.ToArray()
.GroupBy(x => x.Item1)
.ToDictionary(x => x.Key, x => x.ToDictionary(t => t.Item2, t => int.Parse(t.Item3)));
With your input result would be:

Create a unique dictionary and add auto increasing integer values

How can I add a unique number to each of the distinct keys?
In the end I want a "collection" of the distinct keys but each key should have also a value which is e.g. the current_index_of_collection + 1
elements.SelectMany(p => p.Properties.Keys).Distinct();
sample output:
Key value
a 1
b 2
c 3
d 4
Are you looking for Select((value, index) => ...)?
https://msdn.microsoft.com/en-us/library/bb534869(v=vs.110).aspx
var dictionary = elements
.SelectMany(p => p.Properties.Keys)
.Distinct()
.Select((key, index) => new {
key = key,
value = index + 1,
})
.ToDictionary(item => item.key, item => item.value);
Or
var array = elements
.SelectMany(p => p.Properties.Keys)
.Distinct()
.Select((key, index) => new KeyValuePair<MyKeyType, int>(key, index + 1))
.ToArray();
You can use the Select overload which has an index field:
string[][] elements = new string[][] { new string[] { "a", "b", "a" } };
var elementsWithIndex = elements.SelectMany(p => p)
.Distinct()
.Select((p, i) => new { Key = p, Value = i + 1 });
Or in your code:
var elementsWithIndex = elements.SelectMany(p => p.Properties.Keys)
.Distinct()
.Select((p, i) => new { Key = p, Value = i + 1 });
You can simply use this.
List<string> keys = new List<string>();
keys.Add("a");
keys.Add("b");
keys.Add("c");
keys.Add("d");
keys.Add("e");
keys.Add("f");
keys.Add("g");
var fields = keys.Distinct().Select ((t,val)=> new { Key= t, Value= (val + 1)});

Iterate and select over two dimensional string array with LINQ

I did console application that must iterate over two dimensional array of strings and select values that contains in user input and show these values in set by "row".
Unfortunately I got error System.Collections.Generic.List '1[System.String]
Here is the code of application:
static void Main(string[] args)
{
string[,] words = new string[,]
{
{ "5", "" },
{ "10", "kare" },
{ "20", "kanojo" },
{ "1", "karetachi" },
{ "7", "korosu" },
{ "3", "sakura" },
{ "3", "" }
};
try
{
var pre = Console.ReadLine();
var r = Enumerable
.Range(0, words.GetLength(0))
.Where(i => words[i, 1] == pre)
.Select(i => words[i, 1])
.OrderBy(i => words[Int32.Parse(i), 0])
.ToList();
Console.Write(r);
}
catch (Exception ex)
{
TextWriter errorWriter = Console.Error;
errorWriter.WriteLine(ex.Message);
}
Console.ReadLine();
}
Your query is incorrect: you try to match each word from the list to the entirety of the user input, which means that you would always pick a single word (assuming there's no duplicates in the 2D array). Since you are sorting the results, however, it appears that you expect there to be more than one word.
To fix this, replace your selection criteria to use Contains, like this:
var r = Enumerable
.Range(0, words.GetLength(0))
.Where(i => pre.Contains(words[i, 1]))
.Select(i => new {i, w=words[i, 1]})
.OrderBy(p => Int32.Parse(words[p.i, 0]))
.Select(p=>p.w)
.ToList();
To display the results in a single line you could use string.Join:
Console.WriteLine("Results: {0}", string.Join(", ", r));
Note: I assume that the exercise requires you to use a 2D array. If there is no such requirement, you could use an array of tuples or anonymous types, letting you avoid parsing of the integer:
var words = new[] {
new { Priority = 5, Word = "" }
, new { Priority = 10, Word = "kare" }
, new { Priority = 20, Word = "kanojo" }
, ... // and so on
};
Demo.
That's not an error, that's what happens when you display the result of calling the ToString function of a List.
(i.e. your statement ran correctly, you just aren't displaying it the way you think.... see?)
Try:
Console.Write(r.Aggregate((a,b) => a + "," + b));
instead of
Console.Write(r);
The following code creates a 2D List as though we had this
myList[][], consisting of [0] = {0,1,2,3} and [1] = {4,5,6,7,8}
List<List<int>> a2DList = new List<List<int>>()
{
new List<int>()
{
0,1,2,3
},
new List<int>()
{
4,5,6,7,8
}
};
The LINQ code
a2DList.SelectMany(s => s).ToArray().Select(s => s))
returns a copy of the 2d array flattened into 1D form.
SelectMany takes each element and projects each member of each element sequentially.
You could then say
var myObj = a2DList.SelectMany(s => s).ToArray().Select(s => s));
IEnumerable myEnumerable = a2DList.SelectMany(s => s).ToArray().Select(s => s));
int [] myArray = a2DList.SelectMany(s => s).ToArray().Select(s => s)).ToArray();
List myList = a2DList.SelectMany(s => s).ToArray().Select(s => s)).ToList();
etc
This is "join"ed by the string operator for printing out to Console
Console.WriteLine(string.Join(",",a2DList.SelectMany(s => s).ToArray().Select(s => s)));
// Output will be "0,1,2,3,4,5,6,7,8"

Make every string unique by appending counter

I have a list of string and I want to make every string in that list unique by appending a number at the end of it. Also, it is case insensitive, so "apple" should be assumed the SAME as "Apple" or "apPlE"
For example:
List<string> input = new List<string>();
input.Add("apple");
input.Add("ball");
input.Add("apple");
input.Add("Apple");
input.Add("car");
input.Add("ball");
input.Add("BALL");
Expected output:
"apple", "ball", "apple2", "Apple3", "car", "ball2", "BALL3"
I need help to develop the logic to produce the output. Thank you.
Edited: I CANNOT have 0 and 1, the repeated string must start with 2, 3, 4...
var newList = input.GroupBy(x => x.ToUpper())
.SelectMany(g => g.Select((s, i) => i == 0 ? s : s + (i+1)))
.ToList();
var str = String.Join(", ", newList);
EDIT
var newList = input.Select((s, i) => new { str = s, orginx = i })
.GroupBy(x => x.str.ToUpper())
.Select(g => g.Select((s, j) => new { s = j == 0 ? s.str : s.str + (j + 1), s.orginx }))
.SelectMany(x => x)
.OrderBy(x => x.orginx)
.Select(x => x.s)
.ToList();

string manipulation to extract substring of particular pattern

I have the following string in c#
Count([AssignedTo]) as [AssignedTo] , Sum([Billing Amount]) as [Billing Amount] , Max([Billing Rate]) as [Billing Rate] , Min([ExecutionDate]) as [ExecutionDate] , Average([HoursSpent]) as [HoursSpent] , [Project], [Sub-Project], [TaskName], [Vendor], [Work Classification], [Work Done], Count([WorkItemType]) as [WorkItemType]
Now I want list of all fields having aggregate function , through string manipulation or linq
output like
Count([AssignedTo])
Sum([Billing Amount])
Max([Billing Rate])
Min([ExecutionDate])
Average([HoursSpent])
Count([WorkItemType])
Perhaps this works for you:
var aggr = new []{ "Count", "Sum", "Max", "Min", "Average"};
var allAggregates = text.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(col => new{ col, token = col.TrimStart().Split().First() })
.Where(x => x.token.Contains('(') && aggr.Any(a => x.token.StartsWith(a, StringComparison.OrdinalIgnoreCase)))
.Select(x => x.token);
DEMO
can i get the field name only which is inside function
I prefer string methods instead of regex if possible:
var allAggregates = text.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(col => new { col, token = col.TrimStart().Split().First() })
.Where(x => x.token.Contains('(') && aggr.Any(a => x.token.StartsWith(a, StringComparison.OrdinalIgnoreCase)))
.Select(x => {
string innerPart = x.token.Substring(x.token.IndexOf('(') + 1);
int index = innerPart.IndexOf(')');
if (index >= 0)
innerPart = innerPart.Remove(index);
return innerPart;
});
var aggregates = new []{ "Count", "Sum", "Max", "Min", "Average"};
var output=Regex.Matches(input,#"(\w+)\(.*?\)")
.Cast<Match>()
.Where(x=>aggregates.Any(y=>y==x.Groups[1].Value))
.Select(z=>z.Value);
This is another way:
string[] funcNames = new string[]{"Sum","Average","Count","Max","Min"};
string s = "Your String";
var output = from v in s.Split(',')
where funcNames.Contains(v.Split('(')[0].Trim())
select v.Split(new string[]{" as "},
StringSplitOptions.RemoveEmptyEntries)[0].Trim();
.Split('(')[1].Split(')')[0]; //this line is to get only field names
//Print results
foreach(var str in output) Console.WriteLine(str);

Categories