I have got a collection. The coll has strings:
Location="Theater=2, Name=regal, Area=Area1"
and so on. I have to extract just the Name bit from the string. For example, here I have to extract the text 'regal'
I am struggling with the query:
Collection.Location.???? (what to add here)
Which is the most short and precise way to do it?
[Edit] : What if I have to add to a GroupBy clause
Collection.GroupBy(????);
Expanding on Paul's answer:
var location = "Theater=2, Name=regal, Area=Area1";
var foo = location
.Split(',')
.Select(x => x.Split('='))
.ToDictionary(x => x[0].Trim(), x => x[1]);
Console.WriteLine(foo["Name"]);
This populates the original string into a dictionary for easy reference. Again, no error checking or anything.
Location.Split(",").Select(x => x.Split("=")[1])
That's the extremely lazy, completely-without-error-handling way to do it :)
The quick and dirty way is a simple IndexOf/Substring extraction:
string location = "Theater=2, Name=regal, Area=Area1";
int startPos = location.IndexOf("Name=") + 5;
int endPos = location.IndexOf(",", startPos);
string name = location.Substring(startPos, endPos - startPos);
If Regex is an option you can use the lookaround constructs to pluck out a precise match. The sample I used below should work great in c#. The nice thing about this is that it will continue to work even if more comma delimited items are added before the name part.
System.Text.RegularExpressions.Match m =
System.Text.RegularExpressions.Regex.Match(
"Theater=2, Name=regal, Area=Area", #"(?<=Name=)[a-zA-Z0-9_ ]+(?=,)");
Console.WriteLine(m.Value);
Another LINQ-style answer (without the overhead of a dictionary):
var name = (from part in location.Split(',')
let pair = part.Split('=')
where pair[0].Trim() == "Name"
select pair[1].Trim()).FirstOrDefault();
re group by (edit):
var records = new[] {
new {Foo = 123, Location="Theater=2, Name=regal, Area=Area1"},
new {Foo = 123, Location="Name=cineplex, Area=Area1, Theater=1"},
new {Foo = 123, Location="Theater=2, Area=Area2, Name=regal"},
};
var qry = from record in records
let name = (from part in record.Location.Split(',')
let pair = part.Split('=')
where pair[0].Trim() == "Name"
select pair[1].Trim()).FirstOrDefault()
group record by name;
foreach (var grp in qry)
{
Console.WriteLine("{0}: {1}", grp.Key, grp.Count());
}
Related
Somewhat similar to this question:
Where do I put the "orderby group.key" in this LINQ statement?
Except I'm using Dynamic.Linq which makes this a bit harder. I have a bunch of data coming from a database and then I'm grouping by some field and then outputing the result. The problem is that the ordering of the groups seems to randomly jump around which isn't very convenient for the end-user. So taking inspiration from the linked question, if I had this:
string[] words = { "boy","car", "apple", "bill", "crow", "brown" };
// note the first non-dynamic select here was just because I don't think dynamic linq
// will support indexing a string like that and it's not an important detail anyway
var wordList = words.Select(w => new {FirstLetter = w[0], Word = w})
.GroupBy("new (FirstLetter)","Word");
foreach(IGrouping<object, dynamic> g in wordList)
{
Console.WriteLine("Words that being with {0}:",
g.Key.ToString().ToUpper());
foreach (var word in g)
Console.WriteLine(" " + word);
}
Console.ReadLine();
How would I get it to order the keys? At least part of the problem is that the dynamic GroupBy returns an IEnumerable. I thought it might be as easy as:
var wordList = words.Select(w => new {FirstLetter = w[0], Word = w})
.GroupBy("new (FirstLetter)","Word")
.OrderBy("Key");
But that gives me a System.ArgumentException (At least one object must implement IComparable.) when it hits the foreach loop.
My actual code in my project is a little more complicated and looks something like this:
var colGroup = row.GroupBy(string.Format("new({0})",
string.Join(",", c)), string.Format("new({0})",
string.Join(",", v)));
Where c is a list of strings that I need to group by and v is a list of strings that I need to select in each group.
Ok - this is one way to do it, but it might be a little to static to be useful. The problem is that I had this part:
.GroupBy("new (FirstLetter)","Word");
Using new because I can't use a value type as a key (I had another question about that: https://stackoverflow.com/a/26022002/1250301). When with the OrderBy("Key") part, the problem is that it doesn't have a way to compare those dynamic types. I could solve it like this:
var wordList = words.Select(w => new {FirstLetter = w[0].ToString(), Word = w})
.GroupBy("FirstLetter","Word")
.OrderBy("Key");
Making the key a string. Or like this:
var wordList = words.Select(w => new {FirstLetter = w[0], Word = w})
.GroupBy("new (FirstLetter as k)","Word")
.OrderBy("Key.k");
Making it order by something (a char) that is comparable.
I can make it work with my actual problem like this (but it's kind of ugly):
var colGroup = row.GroupBy(string.Format("new({0})", string.Join(",", c)),
string.Format("new({0})", string.Join(",", v)))
.OrderBy(string.Join(",", c.Select(ob => string.Format("Key.{0}", ob))));
I am not sure what you are trying to do, but is that syntax even compiling?
try:
string[] words = { "boy","car", "apple", "bill", "crow", "brown" };
var wordList = words.Select(w => new {FirstLetter = w[0], Word = w})
.GroupBy(x => x.FirstLetter, x => x.Word)
.OrderBy(x => x.Key);
How can I get the string from a list that best match with a base string using the Levenshtein Distance.
This is my code:
{
string basestring = "Coke 600ml";
List<string> liststr = new List<string>
{
"ccoca cola",
"cola",
"coca cola 1L",
"coca cola 600",
"Coke 600ml",
"coca cola 600ml",
};
Dictionary<string, int> resultset = new Dictionary<string, int>();
foreach(string test in liststr)
{
resultset.Add(test, Ldis.Compute(basestring, test));
}
int minimun = resultset.Min(c => c.Value);
var closest = resultset.Where(c => c.Value == minimun);
Textbox1.Text = closest.ToString();
}
In this example if I run the code I get 0 changes in string number 5 from the list, so how can I display in the TextBox the string itself?
for exemple : "Coke 600ml" Right now my TextBox just returns:
System.Linq.Enumerable+WhereEnumerableIterator`1
[System.Collections.Generic.KeyValuePair`2[System.String,System.Int32]]
Thanks.
Try this
var closest = resultset.First(c => c.Value == minimun);
Your existing code is trying to display a list of items in the textbox. I looks like it should just grab a single item where Value == min
resultset.Where() returns a list, you should use
var closest = resultset.First(c => c.Value == minimun);
to select a single result.
Then the closest is a KeyValuePair<string, int>, so you should use
Textbox1.Text = closest.Key;
to get the string. (You added the string as Key and changes count as Value to resultset earilier)
There is a good solution in code project
http://www.codeproject.com/Articles/36869/Fuzzy-Search
It can be very much simplified like so:
var res = liststr.Select(x => new {Str = x, Dist = Ldis.Compute(basestring, x)})
.OrderBy(x => x.Dist)
.Select(x => x.Str)
.ToArray();
This will order the list of strings from most similar to least similar.
To only get the most similar one, simply replace ToArray() with First().
Short explanation:
For every string in the list, it creates an anonymous type which contains the original string and it's distance, computed using the Ldis class. Then, it orders the collection by the distance and maps back to the original string, so as to lose the "extra" information calculated for the ordering.
I'm having a string like "a.b.c.d.e".
If I want to get an array like "a.b.c.d.e", "b.c.d.e", "c.d.e", "d.e", "e" in C#. What's the simplest approach?
Something like this will do:
var stringParts = input.Split('.');
var result = Enumerable.Range(0, stringParts.Length)
.Select(i => string.Join(".", stringParts.Skip(i)));
But like I said in my comment, please show the code you came up with and why you want to make it a one-liner, which usually doesn't serve any benefit. This isn't codegolf.
If you really do it with one statement, you can try this:
var str = "a.b.c.d.e";
var parts = str.Split('.')
.Select((x,idx) => new { idx })
.Select(p => string.Join(".",
str.Split('.').Skip(p.idx))).ToList();
This could be more efficient if you use Split first:
var parts = str.Split('.');
var result = parts
.Select((x,idx) => new { idx })
.Select(p => string.Join(".",
parts.Skip(p.idx))).ToList();
You can also do it without creating anonymous type(s), just create an int variable:
int i = 0;
var result = parts
.Select(p => string.Join(".", parts.Skip(i++)))
.ToList();
This is fairly neat:
var text = "a.b.c.d.e";
var results =
text
.Split('.')
.Reverse()
.Scan("", (a, x) => x + "." + a)
.Select(x => x.TrimEnd('.'))
.Reverse();
You do need to add the Microsoft Reactive Extensions Team's "Interactive Extensions" to get the Scan operator. Use NuGet and look for "Ix-Main".
I actually kind of like this question, not necessarily production but a bit of brain-bendy fun:
"a.b.c.d.e".Split('.').Reverse()
.Aggregate(Enumerable.Empty<string>(), (acc, c) =>
acc.Concat(new [] { c+(acc.LastOrDefault()??"") })
).Reverse()
Dotnetfiddle
What this does is move through each character in the split array and build up a new array by prepending the last value in the array with the current character. It's a fairly common functional programming technique.
Well, this is how I might write it.. I know, not "one line", but if you're gonna use (and I do recommend) a method anyway..
IEnumerable<string> AllComponentPartsForward (string s) {
IEnumerable<string> p = s.Split('.');
while (p.Any()) {
yield return string.Join(".", p); // p.ToArray() for .NET 3.5
p = p.Skip(1);
}
}
(I suppose it could be "more efficient" with IndexOf/Substring, but that's also harder for me to write and reason about!)
I have UK postcodes data and I would like to sort them alphabeticaly, when I do that the result is as follows;
N10-XX
N1-XX
N2-XX
N3-XX
N4-XX
N5-XX
What I want is that as follows;
N1-XX
N2-XX
N3-XX
N4-XX
N5-XX
N10-XX
Basicaly I need to add 0 at the begining of the number if it is 1 digit. like N1 should be N01 to be able to do that, what is the regexp pattern for that?
Many thanks.
Well if you are bent on using Regex, then this should do it
var text = #"N10-XX
N1-XX
N2-XX
N3-XX
N4-XX
N5-XX";
text = Regex.Replace(text, #"^N(\d)-", "N0$1-", RegexOptions.Multiline);
that said you obviously will be altering the original data, so I am not sure if this is even applicable
If you want to sort numerically, but preserve the original data, then you may need to do something like this
text.Split('\n')
.Select(o => new { Original = o, Normal = Regex.Replace(o, #"^N(\d)-", "N0$1-", RegexOptions.Compiled)})
.OrderBy(o => o.Normal)
.Select(o => o.Original)
I'm not sure from the example which numbers in the post code need to be ordered. here is some regex examples for valid uk post codes http://blogs.creative-jar.com/post/Valid-UK-Postcdoe-formats.aspx. if you incorporate this using the method above you should be able to do it.
Here is a sort function returning original string in natural(?) order.
List<string> list1 = new List<string>{ "N10-XX","N1-XX","N2-XX","N3-XX","N4-XX","N5-XX" };
List<string> list2 = new List<string>() { "File (5).txt", "File (1).txt", "File (10).txt", "File (100).txt", "File (2).txt" };
var sortedList1 = MySort(list1).ToArray();
var sortedList2 = MySort(list2).ToArray();
public static IEnumerable<string> MySort(IEnumerable<string> list)
{
int maxLen = list.Select(s => s.Length).Max();
Func<string, char> PaddingChar = s => char.IsDigit(s[0]) ? ' ' : char.MaxValue;
return
list.Select(s =>
new
{
OrgStr = s,
SortStr = Regex.Replace(s, #"(\d+)|(\D+)", m => m.Value.PadLeft(maxLen, PaddingChar(m.Value)))
})
.OrderBy(x => x.SortStr)
.Select(x => x.OrgStr);
}
I have a string containing up to 9 unique numbers from 1 to 9 (myString) e.g. "12345"
I have a list of strings {"1"}, {"4"} (myList) .. and so on.
I would like to know how many instances in the string (myString) are contained within the list (myList), in the above example this would return 2.
so something like
count = myList.Count(myList.Contains(myString));
I could change myString to a list if required.
Thanks very much
Joe
I would try the following:
count = mylist.Count(s => myString.Contains(s));
It is not perfectly clear what you need, but these are some options that could help:
myList.Where(s => s == myString).Count()
or
myList.Where(s => s.Contains(myString)).Count()
the first would return the number of strings in the list that are the same as yours, the second would return the number of strings that contain yours. If neither works, please make your question more clear.
If myList is just List<string>, then this should work:
int count = myList.Count(x => myString.Contains(x));
If myList is List<List<string>>:
int count = myList.SelectMany(x => x).Count(s => myString.Contains(s));
Try
count = myList.Count(s => s==myString);
This is one approach, but it's limited to 1 character matches. For your described scenario of numbers from 1-9 this works fine. Notice the s[0] usage which refers to the list items as a character. For example, if you had "12" in your list, it wouldn't work correctly.
string input = "123456123";
var list = new List<string> { "1", "4" };
var query = list.Select(s => new
{
Value = s,
Count = input.Count(c => c == s[0])
});
foreach (var item in query)
{
Console.WriteLine("{0} occurred {1} time(s)", item.Value, item.Count);
}
For multiple character matches, which would correctly count the occurrences of "12", the Regex class comes in handy:
var query = list.Select(s => new
{
Value = s,
Count = Regex.Matches(input, s).Count
});
try
var count = myList.Count(x => myString.ToCharArray().Contains(x[0]));
this will only work if the item in myList is a single digit
Edit: as you probably noticed this will convert myString to a char array multiple times so it would be better to have
var myStringArray = myString.ToCharArray();
var count = myList.Count(x => myStringArray.Contains(x[0]));