-I have two string(non-space):
string input1 = "bike2car5ship86plan3";
string input2 = "car382bike50ship92yoyo2";
-I've tried regular expressions Match and Linq, but my result is not like hoping:
-How to make them like this result:
Bike 2 x 50 = 100
Car 5 x 382 = 1910
Ship 86 x 92 = 7912
-----------------------------
Total = 9922
Note: plan3 and yoyo2 does not appear in both input1 and input2 so they don't enter the result written.
Try using Linq and regular expressions, something like this:
string input1 = "bike2car5ship86plan3";
string input2 = "car382bike50ship92yoyo2";
var inputs1 = Regex
.Matches(input1, "([a-zA-Z]+)([0-9]+)")
.OfType<Match>()
.ToDictionary(match => match.Groups[1].Value,
match => int.Parse(match.Groups[2].Value));
var inputs2 = Regex
.Matches(input2, "([a-zA-Z]+)([0-9]+)")
.OfType<Match>()
.ToDictionary(match => match.Groups[1].Value,
match => int.Parse(match.Groups[2].Value));
TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo;
var data = inputs1
.Where(pair => inputs2.ContainsKey(pair.Key))
.OrderBy(pair => pair.Key)
.Select(pair => new {
Name = textInfo.ToTitleCase(pair.Key),
Quantity = pair.Value,
Price = inputs2[pair.Key],
Total = pair.Value * inputs2[pair.Key], })
.ToArray();
string table = string.Join(Environment.NewLine, data
.Select(item => string.Format("{0,-5} {1,3} x {2,3} = {3,4}",
item.Name, item.Quantity, item.Price, item.Total)));
string result = string.Join(Environment.NewLine,
table,
new string('-', 25),
string.Format("Total = {0}", data.Sum(pair => pair.Total)));
Console.Write(result);
Outcome:
Bike 2 x 50 = 100
Car 5 x 382 = 1910
Ship 86 x 92 = 7912
-------------------------
Total = 9922
This is simple regex, but you have to do it in multiple steps:
separate every occurrence of word+number
separate the number from word
compare and multiply the findings
To step 1:
as regex you can use "[a-zA-Z]*\d*"
[a-zA-Z]* allows any number of characters followed by a number (\d*)
To step 2:
you can separate the number from the name also with regex ("\d*")
To step 3:
you can add the Matches from the first string to a dictionary like:
myDictionaryForString1.Add(name, value);
and then multiply the values with your values from the second string (same procedure as for the first string to get the values) like:
if (myDictionaryForString1.ContainsKey(name)) {
myResultDictionary.Add(name, myDictionaryForString1[name] * value);
}
Related
I am trying to figure out a regex to use to split a string into 2 character substring.
Let's say we have the following string:
string str = "Idno1";
string pattern = #"\w{2}";
Using the pattern above will get me "Id" and "no", but it will skip the "1" since it doesn't match the pattern. I would like the following results:
string str = "Idno1"; // ==> "Id" "no" "1 "
string str2 = "Id n o 2"; // ==> "Id", " n", " o", " 2"
Linq can make easy the code. Fiddle version works
The idea: I have a chunkSize = 2 as your requirement, then, Take the string at the index (2,4,6,8,...) to get the chunk of chars and Join them to string.
public static IEnumerable<string> ProperFormat(string s)
{
var chunkSize = 2;
return s.Where((x,i) => i % chunkSize == 0)
.Select((x,i) => s.Skip(i * chunkSize).Take(chunkSize))
.Select(x=> string.Join("", x));
}
With the input, I have the output
Idno1 -->
Id
no
1
Id n o 2 -->
Id
n
o
2
Linq is really better in this case. You can use this method - it will allow to split string in chunks of arbitrary size:
public static IEnumerable<string> SplitInChunks(string s, int size = 2)
{
return s.Select((c, i) => new {c, id = i / size})
.GroupBy(x => x.id, x => x.c)
.Select(g => new string(g.ToArray()));
}
But if you are bound to regex, use this code:
public static IEnumerable<string> SplitInChunksWithRegex(string s, int size = 2)
{
var regex = new Regex($".{{1,{size}}}");
return regex.Matches(s).Cast<Match>().Select(m => m.Value);
}
I'm trying to create a regex pattern to grab all the numbers from a given string which are in between square brackets and separated by commas. The output should be like so,
Number1 = 45
Number2 = 66
And so on... All I have so far is a pattern that greedy grabs everything in between square brackets.
string input3;
//string pattern = #"\b\w+es\b";
string pattern = #"\[(.*?)\]";
//Regex regex = new Regex("[*]");
Console.WriteLine("Enter string to search: ");
input3 = Console.ReadLine();
//Console.WriteLine(input3);
List<string> substrings = new List<string>();
int count = 1;
foreach (Match match in Regex.Matches(input3, pattern)) {
string substring = string.Format("Number{0} = '{1}'",count,match);
count++;
Console.WriteLine(substring);
substrings.Add(substring);
}
string[] subStringArray = substrings.ToArray();
}
Should I just create two patterns, the greedy one and then a second pattern to search the greedy output for all numbers separated by commas? Or would it be more efficient to just create a single pattern?
You said that your string is
string which are in between square brackets and separated by commas.
I guess the input is something like that
[1,2,3,4,5,6]
So you can use this regex to get numbers
var numbers = Regex.Match("[1,2,3,4,5,6]", #"\[(?<numbers>[\d,]+)\]").Groups["numbers"].Value;
And then split by , to get a collection of numbers
var collectionOfNumbers = numbers.Split(',');
And to display this string Number1 = 45
Lets us a litle bit of LINQ to do that
C# 6 syntax
var strings = numbers.Split(',').Select((number, i) => $"Number{i + 1} = {number}");
Console.WriteLine(string.Join("\n", strings))
C# <= 5 syntax
var strings = numbers.Split(',').Select((number, i) => string.Format("Number{0} = {1}", i+1, number));
Console.WriteLine(string.Join("\n", strings))
And this is the ouput
Number1 = 1
Number2 = 2
Number3 = 3
Number4 = 4
Number5 = 5
Number6 = 6
Another example with inpunt: Foo Bar [45,66]
C# 6 syntax
var numbers = Regex.Match("Foo Bar [45,66]", #"\[(?<numbers>[\d,]+)\]").Groups["numbers"].Value;
var strings = numbers.Split(',').Select((number, i) => $"Number{i + 1} = {number}");
Console.WriteLine(string.Join("\n", strings))
C# <= 5 syntax
var numbers = Regex.Match("Foo Bar [45,66]", #"\[(?<numbers>[\d,]+)\]").Groups["numbers"].Value;
var strings = numbers.Split(',').Select((number, i) => string.Format("Number{0} = {1}", i+1, number));
Console.WriteLine(string.Join("\n", strings))
The output is
Number1 = 45
Number2 = 66
I have a single string that i want to compare against a list of strings to find the best match.
For example,
string search = "Orange Black Red One Five"
the List of strings could contain the following
l[0] = "Orange Seven Three Black"
l[1] = " Nine Eight Seven Six"
l[2] = " Black Blue Purple Red Five Four Nine Ten"
l[0] contains 2 matches
l[1] contains 0 matches
l[2] contains 3 matches
so the program would choose l[2] as the best match, with a 60% match.
How would I compare two strings like this?
var s = search.Split(new string[] { " "}, StringSplitOptions.RemoveEmptyEntries);
var res1 = (from string part in l
select new
{
list = part,
count = part.Split(new char[] {' '}).Sum(p => s.Contains(p) ? 1 : 0)
}).OrderByDescending(p=> p.count).First();
Console.Write(res1.count);
Split the strings in to arrays.
Determine the number of matches.
Divide.
...
Profit!
Code:
double Compare(string a, string b)
{
var aWords = a.Split(' ');
var bWords = b.Split(' ');
double matches = (double)aWords.Count(x => bWords.Contains(x));
return matches / (double)aWords.Count();
}
Edit: Or, if you just want to get the match count...
int Matches(string a, string b)
{
var aWords = a.Split(' ');
var bWords = b.Split(' ');
return aWords.Count(x => bWords.Contains(x));
}
Ok, so the title isn't really the best. But here's my problem:
I've made a little program that writes a few names and an integer to a .txt document when certain event occurs from an extern program.
The thing is that a name can show up in several lines in the document, so I want to sumarize the integers for each specific person so that I get the total amount of points for him/her and then sort it.
For example:
The original line:
Aaaa Aaa 5
Bbbb Bbb 7
Cccc Ccc 2
Aaaa Aaa 4
Cccc Ccc 4
Bbbb Bbb 1
Dddd Ddd 1
The output I want:
1. Aaaa Aaa 9
2. Bbbb Bbb 8
3. Cccc Ccc 6
4. Dddd Ddd 1
Is there any way to do this in C#?
I've tried to read in every single line in the file and search for the name of a person. But that doesn't really help and I don't know how to solve this.
Any advice?
Obviously, the text is somewhat a key you use to sum up the numbers, so why not use a Dictionary<string, int> to sum up first and write later?
Example:
Dictionary<string, int> sums = new Dictionary<string, int>();
...
if (sums.ContainsKey(theNewString))
sums[theNewString] += theNewNumber;
else
sums[theNewString] = theNewNumber;
And when you know you're done, write the file. You can also re-write the file after every update of the dictionary, but please remember that the dictionary will grow and grow if you don't purge it.
Also: This won't work if the program is restarted, unless you create a new file every time the program starts. Otherwise you'd have to read an existing file into the dictionary when the program starts to continue summing up.
This Linq query returns the desired result as IEnumerable<string>:
IEnumerable<string> lineGroups = File.ReadLines(path)
.Select((l, i) => new { Line = l, Parts = l.Split() })
.Select(x => new
{
Number = x.Parts.ElementAtOrDefault(2).TryGetInt() ?? 1,
Col1 = x.Parts.ElementAtOrDefault(0),
Col2 = x.Parts.ElementAtOrDefault(1),
x.Line,
x.Parts
})
.GroupBy(x =>new { x.Col1, x.Col2 })
.Select((g, groupIndex) =>
string.Format("{0}. {1} {2} {3}",
groupIndex + 1, g.Key.Col1, g.Key.Col2, g.Sum(x => x.Number)));
output:
foreach (var grp in lineGroups)
Console.WriteLine(grp);
This is the output:
1. Aaaa Aaa 9
2. Bbbb Bbb 8
3. Cccc Ccc 2 // different than your desired ouput but seems to be correct
4. Dddd Ddd 1
These are my extension methods that i use in Linq queries to Try-Parse a string to common value type like int(as above). It return a nullable type if it was not parsable:
public static class NumericExtensions
{
public static bool IsBetween(this int value, int fromInclusive, int toInclusive)
{
return value >= fromInclusive && value <= toInclusive;
}
public static Decimal? TryGetDecimal(this string item)
{
Decimal d;
bool success = Decimal.TryParse(item, out d);
return success ? (Decimal?)d : (Decimal?)null;
}
public static int? TryGetInt(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
public static bool TryGetBool(this string item)
{
bool b = false;
Boolean.TryParse(item, out b);
return b; ;
}
public static Version TryGetVersion(this string item)
{
Version v;
bool success = Version.TryParse(item, out v);
return v;
}
}
Create a dictionary with the keys as the names. As value of each item in the dictionary, use the integer and add it to (the value of) an already existing key (or not).
lines.GroupBy(line => string.Join(" ", line.Split().Take(2)))
.Select((g, index) =>
string.Format("{0}. {1} {2}",
index,
g.Key,
g.Sum(line => int.Parse(line.Split().Last()))));
var results = File.ReadAllLines("filename.txt")
.Select(x => x.Split())
.GroupBy(y => new { y1 = y[0], y2 = y[1] })
.Select(g => new { g.Key.y1, g.Key.y2, Sum = g.Sum(v => int.Parse(v[2])) })
.OrderByDescending(p => p.Sum)
.Select(m => m.y1 + " " + m.y2 + " " + m.Sum).ToList();
something like that (not so elegant, but not dependant on the number of spaces)
var lines = File.ReadAllLines(#"<pathToFile>");
var result = lines
.Select(m => m.Split(' '))
.Select(x => new {
text = string.Join(" ", x.Where(z => z != x.Last())),
val = Convert.ToInt32(x.Last())
})
.GroupBy(x => x.text)
.Select(g => new {
text =g.Key,
sum = g.Sum(z => z.val)
}).ToList();
Just another solution with LINQ and regular expressions (for verifying line format and getting names and values from it):
Regex regex = new Regex(#"^(?<name>.*)\s(?<value>\d+)$");
var query = from line in File.ReadLines(file_name)
let match = regex.Match(line)
where match.Success
select new {
Name = match.Groups["name"].Value,
Value = match.Groups["value"].Value
} into item
group item by item.Name into g
orderby g.Key
select new {
Name = g.Key,
Total = g.Sum(x => Int32.Parse(x.Value))
};
Value overflow is not verified here. If it is possible that some values are bigger than Int32.MaxValue, then change sum calculation to
g.Sum(x => { int value; return Int32.TryParse(x.Value, out value) ? value : 0; })
Input 1: List<string>, e.g:
"hello", "world", "stack", "overflow".
Input 2: List<Foo> (two properties, string a, string b), e.g:
Foo 1:
a: "Hello there!"
b: string.Empty
Foo 2:
a: "I love Stack Overflow"
b: "It's the best site ever!"
So i want to end up with a Dictionary<string,int>. The word, and the number of times it appears in the List<Foo>, either in the a or the b field.
Current first-pass/top of my head code, which is far too slow:
var occurences = new Dictionary<string, int>();
foreach (var word in uniqueWords /* input1 */)
{
var aOccurances = foos.Count(x => !string.IsNullOrEmpty(x.a) && x.a.Contains(word));
var bOccurances = foos.Count(x => !string.IsNullOrEmpty(x.b) && x.b.Contains(word));
occurences.Add(word, aOccurances + bOccurances);
}
Roughly:
Build a dictionary (occurrences) from the first input, optionally with a case-insensitive comparer.
For each Foo in the second input, use RegEx to split a and b into words.
For each word, check if the key exists in occurrences. If it exists, increment and update the value in the dictionary.
You could try concating the two strings a + b. Then doing a regex to pull out all the words into a collection. Then finally indexing that using a group by query.
For example
void Main()
{
var a = "Hello there!";
var b = "It's the best site ever!";
var ab = a + " " + b;
var matches = Regex.Matches(ab, "[A-Za-z]+");
var occurences = from x in matches.OfType<System.Text.RegularExpressions.Match>()
let word = x.Value.ToLowerInvariant()
group word by word into g
select new { Word = g.Key, Count = g.Count() };
var result = occurences.ToDictionary(x => x.Word, x => x.Count);
Console.WriteLine(result);
}
Example with some changes suggested...
Edit. Just reread the requirement....kinda strange but hey...
void Main()
{
var counts = GetCount(new [] {
"Hello there!",
"It's the best site ever!"
});
Console.WriteLine(counts);
}
public IDictionary<string, int> GetCount(IEnumerable<Foo> inputs)
{
var allWords = from input in inputs
let matchesA = Regex.Matches(input.A, "[A-Za-z']+").OfType<System.Text.RegularExpressions.Match>()
let matchesB = Regex.Matches(input.B, "[A-Za-z']+").OfType<System.Text.RegularExpressions.Match>()
from x in matchesA.Concat(matchesB)
select x.Value;
var occurences = allWords.GroupBy(x => x, (x, y) => new{Key = x, Count = y.Count()}, StringComparer.OrdinalIgnoreCase);
var result = occurences.ToDictionary(x => x.Key, x => x.Count, StringComparer.OrdinalIgnoreCase);
return result;
}