Change the foreach loop into the LINQ query - c#

I stuck in an easy scenario. I have a List<string> object, all of its items has the body of:
item_1_2_generatedGUID //generatedGUID is Guid.NewGuid()
but there may be much more numbers
item_1_2_3_4_5_generatedGUID etc
now, I'm wondering how to change that loop into the LINQ's query. Any ideas ?
string one = "1"; //an exaplme
string two = "2"; //an exaplme
foreach (var item in myStringsList)
{
string[] splitted = item.Split(new char[] { '_' },
StringSplitOptions.RemoveEmptyEntries);
if(splitted.Length >= 3)
{
if(splitted[1] == one && splitted[2] == two)
{
resultList.Add(item);
}
}
}

var result = from s in lst
let spl = s.Split('_')
where spl.Length >= 3 && spl[1] = one && spl[2] == two
select s;

Try this:
var query = from item in myStringsList
let splitted = item.Split(new[] { '_' }, SSO.RemoveEmptyEntries)
where splitted.Length >= 3
where splitted[1] == one && splitted[2] == two
select item;
var resultList = query.ToList();

This is a different approach:
var items = myStringsList.
Where(x => x.Substring(x.IndexOf("_")).StartsWith(one+"_"+two+"_"));
You probably will need to add a +1 in the IndexOf, but I'm not sure.
What it does is:
Removes the first item (that's the substring for). In your example, it should be "1_2_3_4_5_generatedGUID"
Checks the string starts with what you are expecting. In your example: 1_2_

Edited: Added the pattern for anything at the first "position"
var result = items.Where(i => Regex.IsMatch(i, "^[^_]_1_2_"));

Related

C# linq query - Where with multiple ANDs

I am trying to query using EF. The user can use up to 3 search words but they are not required. How do I write an EF query that will work as an AND for all of the search words that are used but be able to remove an AND for any search word that is empty?
Example, I want the following to return the first two elements in the array for s1='mobile', s2='', and s3='laptop'. It's not returning any. It should return the first two if s2 is changed to s2='burke'.
Example:
using System;
using System.Linq;
public class Simple {
public static void Main() {
string[] names = { "Burke laptop mobile", "laptop burke mobile", "Computer Laptop",
"Mobile", "Ahemed", "Sania",
"Kungada", "David","United","Sinshia" };
//search words
string s1 = "mobile";
string s2 = "";
string s3 = "laptop";
var query = from s in names
where (!string.IsNullOrEmpty(s1) && s.ToLower().Contains(s1))
&& (!string.IsNullOrEmpty(s2) && s.ToLower().Contains(s2))
&& (!string.IsNullOrEmpty(s3) && s.ToLower().Contains(s3))
orderby s
select s.ToUpper();
foreach (string item in query)
Console.WriteLine(item);
}
}
Instead of trying to write a single where statement, you can optionally extend an existing IQueryable<T> (or IEnumerable<T> like our code sample would be using) by chaining Where statements.
var query = names;
if (!string.IsNullOrEmpty(s1))
{
query = query.Where(x => x.Contains(s1));
}
if (!string.IsNullOrEmpty(s2))
{
query = query.Where(x => x.Contains(s2));
}
// ... (or make a foreach loop if s1,s2,s3 were an array.
var results = query.OrderBy(x => x).Select(x => x.ToUpper());
Chaining Where like this is equivalent to "anding" all where predicates together.
EDIT:
To update why your specific implementation doesn't work is because your && operators are incorrect for the given use-case.
(string.IsNullOrEmpty(s1) || s.ToLower().Contains(s1)) &&
(string.IsNullOrEmpty(s2) || s.ToLower().Contains(s2)) &&
(string.IsNullOrEmpty(s3) || s.ToLower().Contains(s3))
Remember that && requires both left and right statements to be true, so in your case !string.IsNullOrEmpty(s2) && s.ToLower().Contains(s2) this is saying that s2 must always be not-empty/null.
Please consider this:
using System;
using System.Linq;
public class Simple {
public static void Main() {
string[] names = { "Burke laptop mobile", "laptop burke mobile", "Computer Laptop",
"Mobile", "Ahemed", "Sania",
"Kungada", "David","United","Sinshia" };
//search words
string s1 = "mobile";
string s2 = "";
string s3 = "laptop";
var query = from s in names
where (s1 != null && s.ToLower().Contains(s1))
&& (s2 != null && s.ToLower().Contains(s2))
&& (s3 != null && s.ToLower().Contains(s3))
orderby s
select s.ToUpper();
foreach (string item in query)
Console.WriteLine(item);
}
}
If you use an array or list instead of multiple strings, you could do something like
List<string> searchWords = new List<string>
{
"mobile",
"",
"laptop"
};
var query = names
.Where(n => searchWords
.Where(s => !string.IsNullOrEmpty(s))
.All(s => n.ToLower().Contains(s)))
.Select(n => n.ToUpper())
.OrderBy(n => n);
This also is more flexible, as you can have any number of search words, without changing the query.

Split and then Joining the String step by step - C# Linq

Here is my string:
www.stackoverflow.com/questions/ask/user/end
I split it with / into a list of separated words:myString.Split('/').ToList()
Output:
www.stackoverflow.com
questions
ask
user
end
and I need to rejoin the string to get a list like this:
www.stackoverflow.com
www.stackoverflow.com/questions
www.stackoverflow.com/questions/ask
www.stackoverflow.com/questions/ask/user
www.stackoverflow.com/questions/ask/user/end
I think about linq aggregate but it seems it is not suitable here. I want to do this all through linq
You can try iterating over it with foreach
var splitted = "www.stackoverflow.com/questions/ask/user/end".Split('/').ToList();
string full = "";
foreach (var part in splitted)
{
full=$"{full}/{part}"
Console.Write(full);
}
Or use linq:
var splitted = "www.stackoverflow.com/questions/ask/user/end".Split('/').ToList();
var list = splitted.Select((x, i) => string.Join("/", a.Take(i + 1)));
Linq with side effect:
string prior = null;
var result = "www.stackoverflow.com/questions/ask/user/end"
.Split('/')
.Select(item => prior == null
? prior = item
: prior += "/" + item)
.ToList();
Let's print it out
Console.WriteLine(string.Join(Environment.NewLine, result));
Outcome:
www.stackoverflow.com
www.stackoverflow.com/questions
www.stackoverflow.com/questions/ask
www.stackoverflow.com/questions/ask/user
www.stackoverflow.com/questions/ask/user/end
Linq without side effects ;)
Enumerable.Aggregate can be used here if we use List<T> as a result.
var raw = "www.stackoverflow.com/questions/ask/user/end";
var actual =
raw.Split('/')
.Aggregate(new List<string>(),
(list, word) =>
{
var combined = list.Any() ? $"{list.Last()}/{word}" : word;
list.Add(combined);
return list;
});
without Linq write below code,
var str = "www.stackoverflow.com/questions/ask/user/end";
string[] full = str.Split('/');
string Result = string.Empty;
for (int i = 0; i < full.Length; i++)
{
Console.WriteLine(full[i]);
}
for (int i = 0; i < full.Length; i++)
{
if (i == 0)
{
Result = full[i];
}
else
{
Result += "/" + full[i];
}
Console.WriteLine(Result);
}

Join List of strings trailing values don't require delimiters if the right most value is null

What im trying to achieve, but cant get my head around is if i have a list of strings say:
{"test","","data","","123","44"}
this should be joined by a character:
test::data::123:44
but if the list at the end is empty dont delimiter it
{"test","","data","","",""}
should be:
test::data
{"test","","","","",""}
should be:
test
{"test","","","","","44"}
should be:
test::::44
however the list can be of varying lengths which adds another level of complexity.
Just exclude the trailing empty elements from the list Count and then Join the remaining using Take:
List<string> input = ...;
int count = input.Count;
while (count > 0 && string.IsNullOrEmpty(input[count - 1]))
count--;
var output = string.Join(":", input.Take(count));
Using the List<T> specific FindLastIndex method, it can be reduced to the following "one liner":
var output = string.Join(":",
input.Take(input.FindLastIndex(s => !string.IsNullOrEmpty(s)) + 1));
First, with your array:
test = test.Where(x => !string.IsNullOrEmpty(x)).ToArray();
where "test" is your array.
then:
string.Join("::", test);
EDIT:
If you're getting your array of strings by splitting another string, consider the following:
string[] strs = myString.split(someDelimeter, StringSplitOptions.RemoveEmptyEntries);
Start by identifying the last element you want, then slice your list and join as you normally would:
var lastElementIndex = strings.Select((s, index) => string.IsNullOrEmpty(s) ? -1 : index).Max();
var prefix = strings.Take(lastElementIndex + 1);
var result = string.Join(":", prefix);
var obj = {"test","","data","","123","44"};
var count = obj.Count;
for (var i = count - 1; i > -1; i--)
{
if (obj[i]==String.Empty) {
obj.RemoveAt(i);
}
else break;
}
var arr = obj.Split(new char[] { ','}, StringSplitOptions.RemoveEmptyEntries);
var output = arr.Join(":", arr);

Find a fixed length string with specific string part in C#

I want to find a string of fixed length with specific substring. But I need to do it like we can do in SQL queries.
Example:
I have strings like -
AB012345
AB12345
AB123456
AB1234567
AB98765
AB987654
I want to select strings that have AB at first and 6 characters afterwards. Which can be done in SQL by SELECT * FROM [table_name] WHERE [column_name] LIKE 'AB______' (6 underscores after AB).
So the result will be:
AB012345
AB123456
AB987654
I need to know if there is any way to select strings in such way with C#, by using AB______.
You can use Regular Expressions to filter the result:
List<string> sList = new List<string>(){"AB012345",
"AB12345",
"AB123456",
"AB1234567",
"AB98765",
"AB987654"};
var qry = sList.Where(s=>Regex.Match(s, #"^AB\d{6}$").Success);
Considering you have an string array:
string[] str = new string[3]{"AB012345", "A12345", "AB98765"};
var result = str.Where(x => x.StartsWith("AB") && x.Length == 8).ToList();
The logic is if it starts with AB, and its length is 8. It is your best match.
this should do it
List<string> sList = new List<string>(){
"AB012345",
"AB12345",
"AB123456",
"AB1234567",
"AB98765",
"AB987654"};
List<string> sREsult = sList.Where(x => x.Length == 8 && x.StartsWith("AB")).ToList();
first x.Length == 8 determines the length and x.StartsWith("AB") determines the required characters at the start of the string
This can be achieved by using string.Startwith and string.Length function like this:
public bool CheckStringValid (String input)
{
if (input.StartWith ("AB") && input.Length == 8)
{
return true;
}
else
{
return false;
}
}
This will return true if string matches your criteria.
Hope this helps.
var strlist = new List<string>()
{
"AB012345",
"AB12345",
"AB123456",
"AB1234567",
"AB98765",
"AB987654"
};
var result = strlist.Where(
s => (s.StartsWith("AB") &&(s.Length == 8))
);
foreach(var v in result)
{
Console.WriteLine(v.ToString());
}

How to retrieve a substring based on a first list match

In a string I need to recover a 7 char substring based on the first match from any item in a list. If a match is not made it should return an empty string.
I have the following code:
List<string> myList = new List<string>()
{
"TNCO",
"TNCB",
"TNIT"
};
string sample = "TNSD102, WHRK301, TNIT301, YTRE234";
//doesn't give an index
bool anyfound = myList.Any(w => sample.Contains(w));
//code that needs replacing
string code = sample.Substring(sample.IndexOf("TNC"), 7);
if (code == "")
{
code = sample.Substring(sample.IndexOf("TNIT"), 7);
}
The list is never likely to be more than 35-40 items and the strings < 50 chars.
Anyone able to point me in the right direction?
string val1 = (sample.Split(',').FirstOrDefault(w => myList.Any(m => w.Contains(m))) ?? string.Empty).Trim();
This gives you an IEnumerable of all matches:
var matches = from code in sample.Split(',')
from w in myList
where code.Trim().StartsWith(w)
select code;
To get the first value use FirstOrDefault. Then use the coalesce operator ?? to return an empty string if there was no match.
string firstMatch = (matches.FirstOrDefault() ?? "").Trim();
With data sets this small, you can simply split the string and search for the first match:
// split the sample string into separate entries
var entries = sample.Split(new char[] {',', ' '},
StringSplitOptions.RemoveEmptyEntries);
// find the first entry starting with any allowed prefix
var firstMatch = entries.FirstOrDefault (
e => myList.Any (l => e.StartsWith(l)));
// FirstOrDefault returns null if there are no matches
if (firstMatch == null)
Console.WriteLine("No match!");
else
Console.WriteLine(firstMatch);
Example output (DEMO):
TNIT301
List<string> myList = new List<string> { "TNCO", "TNCB", "TNIT" };
string sample = "TNSD102, WHRK301, TNIT301, YTRE234";
string[] sampleItems = sample.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
var results = myList
.Select(prefix => sampleItems
.FirstOrDefault(item => item.StartsWith(prefix)) ?? "");
Running this code here returns an Index of 2 based on what you are trying to find.
int keyIndex = myList.FindIndex(w => samples.Contains(w));
TNIT301 this is the indexed string value
you could also do the following to return the string value in index position of keyIndex variable value.
var subStrValue = samples.Split(',')[keyIndex];

Categories