LINQ: How do I compare two elements? - c#

I have a list of strings and if one element is a substring of another element, I want to remove that shorter element.
So,
{abc, def, ghi, ab, cd, ef} => {abc, def, ghi, cd}
I tried:
list = list.Where((x, y) => x.Item1 != y.Item1 && x.Item1.Contains(y.Item1) == false);
but somehow y is an integer.

The overload of Where that you used is for filtering with an element and the index. You need to use the "regular" Where, like this:
var res = list.Where(x => !list.Any(y => x != y && y.Contains(x)));
Demo.

Related

Count elements in array of arrays equal to specified value

I have array of arrays. Suppose I want to count how much elements out of all 9 is equal to "a".
string[][] arr = new string[3][] {
new string[]{"a","b","c"},
new string[]{"d","a","f"},
new string[]{"g","a","a"}
};
How can I do it using Enumerable extension methods (Count, Where, etc)?
You simply need a way to iterate over the subelements of the matrix, you can do this using SelectMany(), and then use Count():
int count = arr.SelectMany(x => x).Count(x => x == "a");
Producing:
csharp> arr.SelectMany(x => x).Count(x => x == "a");
4
Or you could Sum() up the counts of the Count()s of each individual row, like:
int count = arr.Sum(x => x.Count(y => y == "a"));
Producing again:
csharp> arr.Sum(x => x.Count(y => y == "a"));
4
You can flatten all arrays into single sequence of strings with SelectMany and then use Count extension which accepts predicate:
arr.SelectMany(a => a).Count(s => s == "a")

making an char array out of string array

Here I have an array. I want to group them by their first letter, and then take out the strings that begins with a and b, char array them.
the following is my attempt:
string[] ab = { "aa", "ab", "bb", "bc", "cd", "ce" };
var aq = ab.GroupBy(i => i[0]).Where(x => x.Key == 'a' && x.Key == 'b').SelectMany(x => x.Value.ToCharArray());
My Problem here is at my last statement, i cant get x.Value which is strange since it belongs to igroup
Also I would like to get a char array answer something like { a,a,a,b,b,b,b,c}
Try this:
var aq = ab.Where(it => it.StartsWith("a") || it.StartsWith("b"))
.SelectMany(it => it.ToCharArray());
First you select the strings that start with 'a' OR 'b', then you turn them into char arrays and concatenate them.
You can accomplish what you want without a GroupBy(), by checking the first character of each string:
var result = ab
.Where(x => x.ToLower()[0] == 'a'
|| x.ToLower()[0] == 'b')
.SelectMany(x => x)
.ToArray();
Please not this will break if you have any null values in your list.
You can also use the StartsWith() extension and pass in the boolean parameter to ignore case along with the culture info:
var result = ab
.Where(x => x.StartsWith("a", true, System.Globalization.CultureInfo.CurrentCulture)
|| x.StartsWith("b", true, System.Globalization.CultureInfo.CurrentCulture))
.SelectMany(x => x)
.ToArray();
Fiddle here

C# Lambda? In list of class find highest property (int) where property (character) = 1

I have a List of Node where Node class has properties:
public int ID;
public MovingObject character;
I need to, maybe using Lambda, iterate the List and get the highest ID where character = X
I tried Linq extension methods GroupBy and OrderByDescending which does give me the highest ID but that leaves out where character = x. Any help please?
You can use the Where method to filter the collection by the objects who has a character 'x'.
var result = items.Where(item => item.character == X).OrderByDescending(item => item.ID).FirstOrDefault();
You should use Where to filter. Then just ordering and getting the first will find the item you want.
var value = myList.Where(x => object.Equals(x.Character, "h"))
.OrderByDescending(x => x.ID)
.FirstOrDefault();
// check for null
Yet another approach: Filter out nodes with "X" character. Find the highest ID among the filtered out nodes. Search for the node with this ID.
The code:
var highestId = nodes.Where(n => n.Character == "X").Max(n => n.ID);
var highestNode = nodes.Single(n => n.ID == highestId);

LINQ: Compare List A and B, if in B, select item in A

I have a list of tags (List A), and a list of tags matched to topics (List B). I want to parse all items in List B and if it's there, have it select the item in List A. I've tried to do a two line and one line statement to do this, but I run into the same problem no matter what I try. Here's my one line attempt of code:
var tags = db.Tags.Where(x=>x.TagID == db.TagLink.Where(y => y.TopicID == incomingTopicID)).ToList();
List A and B have a common column of Tag ID.
Any suggestions?
Update
The structure of db.TagLink are these columns: TagLinkID(Key), TopicID, TagID.
The structure of db.Tags are these columns: TagID, TagName, IsTagScored.
There are several ways you could go about it. Here's one approach:
var tags = db.Tags
.Where(x=>db.TagLink
.Any(y => y.TagId == x.TagId && y.TopicID == incomingTopicID))
.ToList();
You could use Join, like this:
var tags = db.TagLink.Where(x => x.TopicID == incomingTopicID)
.Join(db.Tag, x => x.TagId, y => y.TagId, (x, y) => y)
.ToList();

Sorting a list of strings by placing words starting with a certain letter at the start

Assuming I have the following list:
IList<string> list = new List<string>();
list.Add("Mouse");
list.Add("Dinner");
list.Add("House");
list.Add("Out");
list.Add("Phone");
list.Add("Hat");
list.Add("Ounce");
Using LINQ how would I select the words containing "ou" and sort the selection such that the words beginning with "ou" are listed at the start and then the words containing but not starting with "ou" are subsequently listed. The list I'm trying to create would be:
Ounce
Out
House
Mouse
I came up with the following but it is not working:
list.Where(x => x.Contains("ou"))
.OrderBy(x => x.StartsWith("ou"))
.Select(x => x);
You're getting a case-sensitive comparison, and also you need OrderByDescending(). A quick and dirty way to achieve the case-insensitivity is ToLowerInvariant():
var result = list.Where(x => x.ToLowerInvariant().Contains("ou"))
.OrderByDescending(x => x.ToLowerInvariant().StartsWith("ou"))
.Select(x => x);
Live example: http://rextester.com/GUR97180
This previous answer shows the correct way to do a case insensitive comparison (ie, dont use my example above, its bad)
Your first mistake is not comparing strings in a case-insensitive way; "Out" and "Ounce" have capital Os and would not return "true" when you use Contains("ou"). The solution is to use ToLower() when checking letters.
list.Where(x => x.ToLower().Contains("ou"))
.OrderByDescending(x => x.ToLower.StartsWith("ou")) //true is greater than false.
.Select(x => x);
Three problems:
You need to assign the result to something, otherwise it is simply discarded.
You need to use OrderByDescending because true sorts after false if you use OrderBy.
You need to use a case-insensitive compare.
Try this:
var needle = "ou";
var stringComparison = StringComparison.OrdinalIgnoreCase;
var query =
from word in list
let index = word.IndexOf(needle, stringComparison)
where index != -1
orderby index
select word;
This will append an empty space to the beginning of words that start with "OU".
var result = list.Where(x => x.ToLowerInvariant().Contains("ou"))
.OrderBy(x => x.ToLowerInvariant()
.StartsWith("ou") ? " " + x : x.Trim());
list = list.Where(x => x.ToLower().Contains("ou"))
.OrderBy(x => !x.ToLower().StartsWith("ou")).ToList();
Or by using the methods of List (changing it from IList to List):
list.RemoveAll(x => !x.ToLower().Contains("ou"));
list.Sort((s1, s2) => -1 * 1.ToLower().StartsWith("ou")
.CompareTo(s2.ToLower().StartsWith("ou")));
I think this is what you're looking for:
list = list.Where(x => x.IndexOf("ou", StringComparison.OrdinalIgnoreCase) >= 0)
.OrderByDescending(x => x.StartsWith("ou", StringComparison.OrdinalIgnoreCase))
.ThenBy(x => x)
.ToList();
Note that instead of converting the strings ToLower (or upper), I use a StringComparison enum (currently OrdinalIgnoreCase). This ensures that it works consistently as expected in any culture. Choose the right case-insensitive comparison depending on your circumstance.
If you prefer the LINQ query syntax that's:
list = (from x in list
where x.IndexOf("ou", StringComparison.OrdinalIgnoreCase) >= 0
orderby x.StartsWith("ou", StringComparison.OrdinalIgnoreCase) descending, x
select x).ToList();
var bla = "ou";
var list = new List<string>{
"Mouse",
"Dinner",
"House",
"Out",
"Phone",
"Hat",
"Ounce"};
var groupa = list.GroupBy(x =>x.ToLower().Contains(bla));
groupa.First().ToList().OrderByDescending(x => x.ToLower().StartsWith(bla));
You can simply call the list.Sort method by passing in an instance of a custom comparer as follows:
public class MyCustomStringComparer: IComparer<string>
{
public int Compare(Entity x, Entity y)
{
int result = 0;
if (x.ToLower().StartsWith("ou") && y.ToLower().StartsWith("ou"))
result = x.Compare(y);
else if (x.ToLower().StartsWith("ou") && !y.ToLower().StartsWith("ou"))
result = -1;
else if (!x.ToLower().StartsWith("ou") && y.ToLower().StartsWith("ou"))
result = 1;
else
result = x.Compare(y);
return (result);
}
}

Categories

Resources