I have a List of strings with the items such-
Titanic, Leonardo Decaprio
Mission Impossible, Tom Cruise
Is there any way such that I can get only the first substring before the comma? In above case the expected output for List<string> should be -
Titanic
Mission Impossible
You can use Split method and then using Select method get only the first part.
list.Select(x=> x.Split(',')[0]);
You can use Substring and IndexOf instead of Split
list.Select(x => x.Substring(0, x.IndexOf(',')));
If you are not sure that every string contains comma you can check it, for example this way
list.Select(x => x.Substring(0, x.IndexOf(',') > 0 ? x.IndexOf(',') : x.Length))
You will also need to call .ToList() method otherwise it will not cast the resulting list into List<string> and throw exception because resulting list would of type System.Collections.Generic.IEnumerable<string> and your's is of List<string
List<string> list = new List<string>() { "Titanic, Leonardo Decaprio", " Mission Impossible, Tom Cruise" };
list = list.Select(x => x.Split(',')[0]).ToList();
Related
This question already has answers here:
How to Count Duplicates in List with LINQ
(7 answers)
Closed 2 years ago.
I currently have what I believe is a lambda function with C# (fairly new to coding & haven't used a lambda function before so go easy), which adds duplicate strings (From FilteredList) in a list and counts the number of occurrences and stores that value in count. I only want the most used word from the list which I've managed to do by the "groups.OrderBy()... etc) line, however I'm pretty sure that I've made this very complicated for myself and very inefficient. As well as by adding the dictionary and the key value pairs.
var groups =
from s in FilteredList
group s by s into g
// orderby g descending
select new
{
Stuff = g.Key,
Count = g.Count()
};
groups = groups.OrderBy(g => g.Count).Reverse().Take(1);
var dictionary = groups.ToDictionary(g => g.Stuff, g => g.Count);
foreach (KeyValuePair<string, int> kvp in dictionary)
{
Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}
Would someone please either help me through this and explain a little bit of this too me or at least point me in the direction of some learning materials which may help me better understand this.
For extra info: The FilteredList comes from a large piece of external text, read into a List of strings (split by delimiters), minus a list of string stop words.
Also, if this is not a lambda function or I've got any of the info in here incorrect, please kindly correct me so I can fix the question to be more relevant & help me find an answer.
Thanks in advance.
Yes, I think you have overcomplicated it somewhat.. Assuming your list of words is like:
var words = new[] { "what's", "the", "most", "most", "most", "mentioned", "word", "word" };
You can get the most mentioned word with:
words.GroupBy(w => w).OrderByDescending(g => g.Count()).First().Key;
Of course, you'd probably want to assign it to a variable, and presentationally you might want to break it into multiple lines:
var mostFrequentWord = words
.GroupBy(w => w) //make a list of sublists of words, like a dictionary of word:list<word>
.OrderByDescending(g => g.Count()) //order by sublist count descending
.First() //take the first list:sublist
.Key; //take the word
The GroupBy produces a collection of IGroupings, which is like a Dictionary<string, List<string>>. It maps each word (the key of the dictionary) to a list of all the occurrences of that word. In my example data, the IGrouping with the Key of "most" will be mapped to a List<string> of {"most","most","most"} which has the highest count of elements at 3. If we OrderByDescending the grouping based on the Count() of each of the lists then take the First, we'll get the IGrouping with a Key of "most", so all we need to do to retrieve the actual word is pull the Key out
If the word is just one of the properties of a larger object, then you can .GroupBy(o => o.Word). If you want some other property from the IGrouping such as its first or last then you can take that instead of the Key, but bear in mind that the property you end up taking might be different each time unless you enforce ordering of the list inside the grouping
If you want to make this more efficient than you can install MoreLinq and use MaxBy; getting the Max word By the count of the lists means you can avoid a sort operation. You could also avoid LINQ and use a dictionary:
string[] words = new[] { "what", "is", "the", "most", "most", "most", "mentioned", "word", "word" };
var maxK = "";
var maxV = -1;
var d = new Dictionary<string, int>();
foreach(var w in words){
if(!d.ContainsKey(w))
d[w] = 0;
d[w]++;
if(d[w] > maxV){
maxK = w;
maxV = d[w];
}
}
Console.WriteLine(maxK);
This keeps a dictionary that counts words as it goes, and will be more efficient than the LINQ route as it needs only a single pass of the word list, plus the associated dictionary lookups in contrast to "convert wordlist to list of sublists, sort list of sublists by sublist count, take first list item"
This should work:
var mostPopular = groups
.GroupBy(item => new {item.Stuff, item.Count})
.Select(g=> g.OrderByDescending(x=> x.Count).FirstOrDefault())
.ToList();
OrderByDescending along with .First() combines your usage of OrderBy, Reverse() and Take.
First part is a Linq operation to read the groups from the FilteredList.
var groups =
from s in FilteredList
group s by s into g
// orderby g descending
select new
{
Stuff = g.Key,
Count = g.Count()
};
The Lambda usage starts when the => signal is used. Basically means it's going to be computed at run time and an object of that type/format is to be created.
Example on your code:
groups = groups.OrderBy(g => g.Count).Reverse().Take(1);
Reading this, it is going to have an object 'g' that represents the elements on 'groups' with a property 'Count'. Being a list, it allows the 'Reverse' to be applied and the 'Take' to get the first element only.
As for documentation, best to search inside Stack Overflow, please check these links:
C# Lambda expressions: Why should I use them? - StackOverflow
Lambda Expressions in C# - external
Using a Lambda Expression Over a List in C# - external
Second step: if the data is coming from an external source and there are no performance issues, you can leave the code to refactor onwards. A more detail data analysis needs to be made to ensure another algorithm works.
I am going to write search query. In that query I have a list of string like this:
var listWords=new List<string>(){"Hello","Home","dog","what"};
and also I have list of customers. How Can I search if customer's Name contains at least one of items in listWords:
Jack Home
Hot dog
what a big dog
Tried:
var prodList = events.Where(x =>listWords.IndexOf(x.Name.Trim().ToLower()) != -1).ToList();
Use .Where and .Any:
var result = events.Where(c => listWords.Any(w => c.Name.Contains(w)));
Problem with your solution is that you are converting your string to lower case but the collection of words has characters in upper case. In that case it will not find a match:
How can I make Array.Contains case-insensitive on a string array?
I have two list namely, list1 and list2
Suppose if my list1 has the following elements,
"first", "second","third"
and my list2 has the following elements,
"element first is present here"
"elements are present in second"
So in this case, the string "first" and "second" are present in the list2.(but not exactly the same as in variablelist).
So in this case, how can I except "first" and "second" and show my result as, only "third" element is not present in the list2?
I am using the following code
var inOnlyVariableList = list1.Except(list2).ToList();
Thanks in Advance
You can't use a set based approach(like Except) if you search substrings. So it's not as efficient but still readable:
var inOnlyVariableList = list1.Where(s => !list2.Any(s2 => s2.Contains(s))).ToList();
String.Contains looks if the given string is contained in the larger string.
If you want to support case insensitive comparison you can use:
var inOnlyVariableList = list1
.Where(s => list2.All(s2 => s2.IndexOf(s, StringComparison.InvariantCultureIgnoreCase) == -1))
.ToList();
I met a Difficulty to sort a list item string containing numbers.
I wish I could automatically sort from smallest to largest.
Here is my code:
// Initialize an array to store the numbers
List<string> tab_num = new List<string>();
tab_num.Add("A.3.2.1");
tab_num.Add("A.3.3.1");
tab_num.Add("A.1.0.1");
tab_num.OrderByDescending(num => num).ToList();
Why my result is false ?...
[1] A.3.2.1
[2] A.3.3.1
[3] A.1.0.1
Expected Result:
[1] A.1.0.1
[2] A.3.2.1
[3] A.3.3.1
Thanks a lot
OrderByDescending means from bigger to lower.
Also you are not assigning your result after sorting.
try this:
List<string> tab_num = new List<string>();
tab_num.Add("A.3.2.1");
tab_num.Add("A.3.3.1");
tab_num.Add("A.1.0.1");
tab_num = tab_num.OrderBy(num => num).ToList();
Because you are sorting alphabetically. You should use OrderBy anyway if you want to order "smallest to largest". You need to parse it to somewhat numerical or to version:
Version v = null;
var ordered = tab_num.Select(tab => new { tab, Versionpart = tab.Substring(2) })
.Where(x => Version.TryParse(x.Versionpart, out v))
.OrderBy(x => v)
.Select(x => x.tab)
.ToList();
(assuming that the version is always found at tab.Substring(2))
You should set result of linq query to any variable (and use OrderBy):
List<string> tab_num = new List<string>();
tab_num.Add("A.3.2.1");
tab_num.Add("A.3.3.1");
tab_num.Add("A.1.0.1");
tab_num = tab_num.OrderBy(num => num).ToList();
tab_num.OrderBy(num => num).ToList() is not perform sorting on source list, but returns sorted list.
Actually you don't need linq here, use Sort method:
tab_num.Sort();
But if you want to make your code work, just assign resulting list to a source list:
tab_num = tab_num.OrderBy(x => x).ToList();
the main problem here is that you are not assigning the result to the list as suggested below.
BTW: This list doesn't contains numbers, it contains string so the comparer uses the alphabetic comparator. you should use OrderBy not OrderByDescending. I suggest you to implement your own comparator since your strings are quite simple and the default comparator will give you wrong responses when numbers grows over 9
I have a list like this:
item.Add("a");
item.Add("as");
item.Add("b");
item.Add("fgs");
item.Add("adsd");
How can I find all items that start with (for example) "a"?
This "a" is not some hardcoded string, so I will need a function that do this for each string.
I try with FindAll, but I did not figured out how it works.
Br, Wolfy
If by "start with" you mean the first char, then:
item.FindAll(i => i[0] == 'a');
if you mean a string (may be other than 1 char) then:
item.FindAll(i => i.StartsWith("a"));
If you want a different comparison, such as case-insensitive, locale-based, etc. then do the relevant IndexOf such as:
item.FindAll(i => i.IndexOf("a", StringComparison.CurrentCultureIgnoreCase) == 0);
All of the above can be easily adapted to be use a relevant char or string variable or parameter.
If you don't need the extra properties and methods provided by a list, then it will be more efficient to use Where than FindAll as FindAll creates a new list, and does so in all at once, while Where will enumerate the matching results as it is iterated through.
Or with LINQ
from i in items where i.StartsWith("a") select i;
for NET2.0 you may use this method:
'pattern' is an argument to look for (f.e. "a")
private List<string> FindAll(List<string> list, string pattern)
{ // returns found results
return list.FindAll(delegate(string item)
{
return item.StartsWith(pattern);
});
}
I thought you have another list that contains the startswith criteria strings. Lets call your items "words" and the other list "keywords". So the below query will return what you want.
List<string> words = new List<string>();
words.Add("a");
words.Add("as");
words.Add("b");
words.Add("fgs");
words.Add("adsd");
List<string> keywords = new List<string>();
keywords.Add("a");
keywords.Add("b");
var result = words.FindAll(o =>
keywords.Any(a => o.StartsWith(a))
);
This result has the words that starts with any of the keyword from keywords.
List<string> item = new List<string>();
item.Add("a");
item.Add("as");
item.Add("b");
item.Add("fgs");
item.Add("adsd");
var res1 = item.FindAll(i => i.StartsWith("a"));
var res2 = item.Where(i => i.StartsWith("a"));
Try this
item.FindAll(i => i.Contains("a"));
This will return a List containting only the filtered strings.