How to return substring of linq results - c#

Let's say I have a list of strings:
originalList = { "XX.one", "XX.two", "YY.three" }
I want to use linq to select and return a list with {"one", "two"}.
if I do for example
resultList = originalList.FindAll(o => o.StartsWith("XX")));
I will get resultList = { "XX.one", "XX.two" } but what I want is resultList = { "one", "two" }
Any way to solve this?
EDIT: Thanks for all who answered, I've chosen the split function of #er-mfahhgk since it does the minimum of manipulation and doesn't depend on size of the prefix.

You can use SelectWith your desired string and then using Split function on Dot (.) you can select the second part like
var resultList = originalList.Where(o => o.StartsWith("XX"))
.Select(x => x.Split('.')[1])
.ToList();
And finally your output will be,
foreach (var item in resultList)
{
Console.WriteLine(item);
}
Console.ReadLine();
Output:

result = originalList.Where(o => o.StartsWith("XX"))
.Select(x=>x.Replace("XX.,""))
.ToList();

You could try this:
resultList = originalList.Where(o => o.StartsWith("XX"))
.Select(x=>x.Substring(3))
.ToList();
( edited to correct wording of Substring )

Related

Getting a list of strings with only the first and last character from another list LINQ

From a given list of strings I need to use LINQ to generate a new sequence of strings, where each string consists of the first and last characters of the corresponding string in the original list.
Example:
stringList: new[] { "ehgrtthrehrehrehre", "fjjgoerugrjgrehg", "jgnjirgbrnigeheruwqqeughweirjewew" },
expected: new[] { "ee", "fg", "jw" });
list2 = stringList.Select(e => {e = "" + e[0] + e[e.Length - 1]; return e; }).ToList();
This is what I've tried, it works, but I need to use LINQ to solve the problem and I'm not sure how to adapt my solution.
just for the sake of completeness here is a version using Zip
var stringList = new string [] { "ehgrtthrehrehrehre", "fjjgoerugrjgrehg", "jgnjirgbrnigeheruwqqeughweirjewew" };
var result = stringList.Zip(stringList, (first, last) => $"{first.First()}{last.Last()}");
As mentioned in the comment that Select is already part of LINQ, you can use this code.var output = arr.Select(x => new string(new char[] { x.First(), x.Last() })).ToList();
Here you go:
var newList = stringList.Select(e => $"{e[0]}{e[e.Length - 1]}").ToList();
Approach with LINQ and String.Remove():
string[] input = new[] { "ehgrtthrehrehrehre", "fjjgoerugrjgrehg", "jgnjirgbrnigeheruwqqeughweirjewew" };
string[] result = input.Select(x => x.Remove(1, x.Length - 2)).ToArray();

Enumerable.Concat not working

Below is the code:
string[] values = Acode.Split(',');
IEnumerable<Test> tst = null;
foreach (string a in values)
{
if (tst== null)
tst = entities.Test.Where(t=> (t.TCode == Convert.ToInt16(a)));
else
tst.Concat(entities.Test.Where(g => (g.TCode == Convert.ToInt16(a))));
}
return tst.ToList();
I am not able to get all the records in tst, it is giving me records only for the last value in array.
So if my array contains 1,2,3,4 I am getting records only for the 4. Whereas i need all the result for 1,2,3 and 4 get appended in tst.
Any help will be appreciated.
Concat doesn't modify anything - it returns a new sequence, which you're currently ignoring.
However, rather than using Concat, you should just use SelectMany to flatten the sequence:
string[] values = Acode.Split(',');
return values.SelectMany(a => entities.Test.Where(t => t.TCode == Convert.ToInt16(a)))
.ToList();
Or more efficiently, convert values into a List<short> and then you can do one query:
List<short> values = Acode.Split(',').Select(x => short.Parse(x)).ToList();
return entities.Test.Where(t => values.Contains(t.TCode)).ToList();
That is because Concat will return a new instance of your enumerable.
Either use in your else :
tst = tst.Concat(...)
Or Change your Enumerable into list from the beginning :
string[] values = Acode.Split(',');
List<Test> tst= new List<Test>;
foreach (string a in values)
{
tst.AddRange(entities.Test.Where(g => (g.TCode == Convert.ToInt16(a))));
}
return tst;

How to use Linq to check if a list of strings contains any string in a list

I'm constructing a linq query that will check is a string in the DB contains any of the strings in a list of strings.
Something like.
query = query.Where(x => x.tags
.Contains(--any of the items in my list of strings--));
I'd also like to know how many of the items in the list were matched.
Any help would be appreciated.
Update: I should have mentioned that tags is a string not a list. And I am adding on a couple more wheres that are not related to tags before the query actually runs. This is running against entity framework.
EDIT: This answer assumed that tags was a collection of strings...
It sounds like you might want:
var list = new List<string> { ... };
var query = query.Where(x => x.tags.Any(tag => list.Contains(tag));
Or:
var list = new List<string> { ... };
var query = query.Where(x => x.tags.Intersect(list).Any());
(If this is using LINQ to SQL or EF, you may find one works but the other doesn't. In just LINQ to Objects, both should work.)
To get the count, you'd need something like:
var result = query.Select(x => new { x, count = x.tags.Count(tag => list.Contains(tag)) })
.Where(pair => pair.count != 0);
Then each element of result is a pair of x (the item) and count (the number of matching tags).
I've done something like this before:
var myList = new List<string>();
myList.Add("One");
myList.Add("Two");
var matches = query.Where(x => myList.Any(y => x.tags.Contains(y)));
like this:
List<string> list = new List<string>();
list.Add("One");
list.Add("Two");
var result = query.Where(x => list.Contains(x.tags));
I am not quite sure from your question if x.tags is a string or list, if it is a list Jon Skeet's answer is correct. If I understand you correctly though x.tags is a string of strings. If so then the solution is:
list.Any(x => x.tags.IndexOf(x) > -1)
to count them do
list.Count(x => x.tags.IndexOf(x) > -1)
var t = new List<string> { "a", "b", "c" };
var y = "a b d";
var res = y.Count(x => t.Contains(x.ToString()));
I faced a similar problem recently and here's how I managed to work it out:
var list = [list of strings];
if (list != null && list.Any())
{
queryable = queryable.Where(x => x.tags != null);
var tagQueries = new List<IQueryable<WhateverTheDbModelIs>>();
foreach (var element in list)
{
tagQueries.Add(queryable.Where(x => x.tags.Contains(element)));
}
IQueryable<WhateverTheDbModelIs> query = tagQueries.FirstOrDefault();
foreach (var tagQuery in tagQueries)
{
query = query.Union(tagQuery);
}
queryable = queryable.Intersect(query);
}
probably not the best option but something a less experienced developer can understand and use

Is there a cleaner way to split delimited text into data structures?

I have this code:
private IEnumerable<FindReplacePair> ConstructFindReplacePairs(string inputFilePath)
{
var arrays = from line in File.ReadAllLines(Path.GetFullPath(inputFilePath))
select line.Split('|');
var pairs = from array in arrays
select new FindReplacePair { Find = array[0], Replace = array[1] };
return pairs;
}
I'm wondering if there is a clean linq syntax to do this operation in only one query, because it feels like there should be.
I tried chaining the from clauses (a SelectMany), but it splits up the data too much and I could not get to the separate arrays to select from (instead I got individual strings one at a time).
IEnumerable<FindReplacePair> ConstructFindReplacePairs(string inputFilePath)
{
return File.ReadAllLines(Path.GetFullPath(inputFilePath))
.Select(line => line.Split('|'))
.Select(array => new FindReplacePair {
Find = array[0],
Replace = array[1]
});
}
OR
IEnumerable<FindReplacePair> ConstructFindReplacePairs(string inputFilePath)
{
return from line in File.ReadAllLines(Path.GetFullPath(inputFilePath))
let array = line.Split('|')
select new FindReplacePair {
Find = array[0], Replace = array[1]
};
}
You can also add where condition to check if array has more than one element.
Not sure if this is cleaner, just a little bit shorter.
IEnumerable<FindReplacePair> allFindReplacePairs = File.ReadLines(inputFilePath)
.Select(l => new FindReplacePair { Find = l.Split('|')[0], Replace = l.Split('|')[1] });
Note that i'm using File.ReadLines which does not need to read all lines into memory first. it works like a StreamReader.
When it comes down to prettifying LINQ, I usually write out simple loop and Resharper will suggest a better LINQ optimisation, e.g.
foreach (var split in File.ReadAllLines(inputFilePath).Select(l => l.Split('|')))
yield return new FindReplacePair { Find = split[0], Replace = split[1] };
R# convertes it to
return File.ReadAllLines(inputFilePath).Select(l => l.Split('|')).Select(split => new FindReplacePair { Find = split[0], Replace = split[1] });
That said you might as well use builtin type, e.g. .ToDictionary(l => l[0], l => l[1]) or add a method on FindReplacePair, i.e.
return File.ReadAllLines(inputFilePath).Select(l => l.Split('|')).Select(FindReplacePair.Create);
public static FindReplacePair Create(string[] split)
{
return new FindReplacePair { Find = split.First(), Replace = split.Last() };
}

Determine if string appears more than once in string array (C#)

I have an array of strings, f.e.
string [] letters = { "a", "a", "b", "c" };
I need to find a way to determine if any string in the array appears more than once.
I thought the best way is to make a new string-array without the string in question and to use Contains,
foreach (string letter in letters)
{
string [] otherLetters = //?
if (otherLetters.Contains(letter))
{
//etc.
}
}
but I cannot figure out how.
If anyone has a solution for this or a better approach, please answer.
The easiest way is to use GroupBy:
var lettersWithMultipleOccurences = letters.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
This will first group your array using the letters as keys. It then returns only those groups with multiple entries and returns the key of these groups. As a result, you will have an IEnumerable<string> containing all letters that occur more than once in the original array. In your sample, this is only "a".
Beware: Because LINQ is implemented using deferred execution, enumerating lettersWithMultipleOccurences multiple times, will perform the grouping and filtering multiple times. To avoid this, call ToList() on the result:
var lettersWithMultipleOccurences = letters.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key).
.ToList();
lettersWithMultipleOccurences will now be of type List<string>.
You can the LINQ extension methods:
if (letters.Distinct().Count() == letters.Count()) {
// no duplicates
}
Enumerable.Distinct removes duplicates. Thus, letters.Distinct() would return three elements in your example.
Create a HashSet from the array and compare their sizes:
var set = new HashSet(letters);
bool hasDoubleLetters = set.Size == letters.Length;
A HashSet will give you good performance:
HashSet<string> hs = new HashSet<string>();
foreach (string letter in letters)
{
if (hs.Contains(letter))
{
//etc. more as once
}
else
{
hs.Add(letter);
}
}

Categories

Resources