LINQ search in list - c#

I have this list
var allPlaces = new[]
{
new { Name = "Red apple", OtherKnownNames = "Green" },
new { Name = "Orange", OtherKnownNames = "" },
new { Name = "Banana", OtherKnownNames = "the" },
}.ToList();
my query is "the apple"
my code does not return me first and third item, query has 2 words separated by a space, I want if any word in query starts with the Name or OtherKnownName should be returned.
var query = "the apple";
var queryParts = query.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
var filteredList =
allPlaces
.Where(p =>
p.Name
.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
.Any(pp => queryParts.Any(qp => qp.StartsWith(pp)))
|| p.OtherKnownNames
.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
.Any(pp => queryParts.Any(qp => qp.StartsWith(pp))))
.ToList();

Assuming you want to ignore case, and accept names that match the beginning of query words (based on your example with StartsWith).
Use an extension method to make splitting without empty entries nicer:
public static string[] SplitNoEmpty(this string s, params char[] seps) => s.Split(seps, StringSplitOptions.RemoveEmptyEntries);
You can simply split up the query string and search for matches:
var qwords = query.SplitNoEmpty(' ');
var ans = allPlaces.Where(p => qwords.Any(qw => (p.Name + " " + p.OtherKnownNames).SplitNoEmpty(' ')
.Any(nw => qw.StartsWith(nw, StringComparison.CurrentCultureIgnoreCase))
)
)
.ToList();

Related

c# substring from a word in quotes

I am trying to use substring to get a value from a string as such:
surname='Smith',name="John"
I basically want to use the text "name" and the quotes to get the value "John"..
Is there a way to do this please?
you could use linq query to get name
var query = #"surname='Smith',name = \""John\""";
var name = query
.Split(',')
.Select(s => new KeyValuePair<string, string>(
s.Split('=').GetValue(0).ToString().Trim(),
s.Split('=').GetValue(1).ToString().Trim()
))
.FirstOrDefault(kvp => kvp.Key == "name").Value;
Console.WriteLine(name);
There are many ways to do that.
That is one:
char[] quotes = { '\'', '\"' };
string input = "surname='Smith',name=\"John\"";
string[] sections = input.Split(',');
for (int i = 0; i < sections.Length; i++)
{
string[] pair = sections[i].Split('=');
if (pair[0] == "surname")
Debug.WriteLine("surname=" + pair[1].Trim(quotes));
if (pair[0] == "name")
Debug.WriteLine("name=" + pair[1].Trim(quotes));
}

LINQ Spliting string into substrings sequentially, with using a delimiter

I have a table of strings, e.g :
string[] list = { "900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org" };
string delimiter = ".";
foreach (var item in cpdomains)
{
var result = item.Split(' ', '.').Aggregate((a,b) => a + delimiter +b);
result.ForEach(e => Console.WriteLine(e));
Console.WriteLine(result);
}
my result now
900.google.mail.com
50.yahoo.com
1.intel.mail.com
5.wiki.org
I want to split every string for substrings like this:
1- 900
2- google.mail.com
3-mail.com
4-com
etc...
How I can do this ?
Thanks in advance
public string[] GetParentDomains(string[] input) {
return input
.SelectMany(s => s.Split(' '))
.SelectMany(s => {
string[] splitDomain = s.Split('.');
return Enumerable.Range(0, splitDomain.Length)
.Select(counter =>
String.Join(".", splitDomain.Skip(counter)))
.ToArray();
})
.ToArray();
}
Output
900
google.mail.com
mail.com
com
50
yahoo.com
com
1
intel.mail.com
mail.com
com
5
wiki.org
org

c# get new list of elements grouped between certain element in list

in c# how do you get a new list of elements grouped by falling in between a certain element. for example if my list was ['visit', 'houston', 'and', 'san', 'antonio', 'and', 'austin', 'and', 'corpus', 'christi']
and i wanted to extract the cities between "and" into a new list grouped between the "ands" so the two word names cities are in a group together
In python you can use itertools but how can you accomplish this in c#?
import itertools as itt
List =['visit', 'houston', 'and', 'san', 'antonio', 'and', 'austin', 'and', 'corpus', 'christi']
>>> [list(g) for k, g in itt.groupby(L, key=lambda word: word=='and') if not k]
results-
[['visit', 'houston'], ['san', 'antonio'], ['austin'], ['corpus', 'christi']]
Combine them into a single string (or leave them that way if that's how they started), then split it by and and split each substring again:
var words = new[] { "visit", "houston", "and", "san", "antonio", "and", "austin", "and", "corpus", "christi" };
var sentence = string.Join(' ', words); // "visit houston and san .... christi"
var cities = sentence.Split("and", StringSplitOptions.None)
.Select(x => x.Split(' ', StringSplitOptions.RemoveEmptyEntries))
.ToArray();
Note that if your input includes spaces in them (like ..., "and", "san antonio", ...) then this may need some adjusting.
For this you can use System.Linq.GroupBy with a little modification to add key as number of "and"s preceding the given word.
Group method:
static string[][] GroupByWord(string[] input, string word)
{
var i = 0;
return input.GroupBy(w =>
{
if (w == word)
{
i++;
return -1;
}
return i;
})
.Where(kv => kv.Key != -1) // remove group with "and" strings
.Select(s => s.ToArray()) // make arrays from groups ["visit", "houston"] for example
.ToArray(); // make arrays of arrays
}
Calling method:
var input = new[] { "visit", "houston", "and", "san", "antonio", "and", "austin", "and", "corpus", "christi" };
var result = GroupByWord(input, "and");
A simpler approach using loops.
IEnumerable<IEnumerable<string>> GetList(IEnumerable<string> source)
{
while(source.Any())
{
var returnValue = source.TakeWhile(x=>!x.Equals("and")).ToList();
yield return returnValue;
source = source.Skip(returnValue.Count()+1);
}
}
You can now do,
var words = new[] { "visit", "houston", "and", "san", "antonio", "and", "austin", "and", "corpus", "christi" };
var result = GetList(words);
Output

reading more than one text file and store the content to an array

i write a c# program that read data from 5 text files and count them according to some given key word
string[] word_1 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D1_H1.txt").Split(' ');
string[] word_2 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D2_H1.txt").Split(' ');
string[] word_3 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D3_H2.txt").Split(' ');
string[] word_4 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D4_H2.txt").Split(' ');
string[] word_5 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D5_H2.txt").Split(' ');
string[] given_doc = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\Given_doc.txt").Split(' ');
this is how i read from text files, after reading that i use for loop and if loop to count each word from hose file
for (int i = 0; i < word_1.Length; i++)
{
string s = word_1[i];
if ("Red".Equals(word_1[i]))
{
//Console.WriteLine(word[i]);
h1_r++;
}
if ("Green".Equals(word_1[i]))
{
h1_g++;
}
if ("Blue".Equals(word_1[i]))
{
h1_b++;
}
}
this is the loop i used to get the count from one file and its works fine, i did this process 5 times to read all files, my question is how can i read those 5 files using one for loop and store them in a array (count of each key word)
thanks in advance !!
LINQ query is your the simplest solution here:
var filenames = new[] { "D1_H1.txt", "D2_H1.txt", "D3_H2.txt" };
var words = new[] { "Red", "Green", "Blue" };
var counters =
filenames.Select(filename => Path.Combine(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment", filename))
.SelectMany(filepath => File.ReadAllLines(filepath))
.SelectMany(line => line.Split(new[] { ' ' }))
.Where(word => words.Contains(word))
.GroupBy(word => word, (key, values) => new
{
Word = key,
Count = values.Count()
})
.ToDictionary(g => g.Word, g => g.Count);
and then you have dictionary of word counter within all files:
int redCount = counters["Red"];
If you want to store counters per each file, you can use slightly modified query:
var filenames = new[] { "D1_H1.txt", "D2_H1.txt", "D3_H2.txt" };
var words = new[] { "Red", "Green", "Blue" };
var counters =
filenames.Select(filename => Path.Combine(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment", filename))
.Select(filepath => new
{
Filepath = filepath,
Count = File.ReadAllLines(filepath)
.SelectMany(line => line.Split(new[] { ' ' }))
.Where(word => words.Contains(word))
.GroupBy(word => word, (key, values) => new
{
Word = key,
Count = values.Count()
})
.ToDictionary(g => g.Word, g => g.Count)
})
.ToDictionary(g => g.Filepath, g => g.Count);
and then use it accordingly:
int redCount = counters[#"C:\Users\(...)\D1_H1.txt"]["Red"];
Copy pasting code is generally not good. It leads to code violating the Don't Repeat Yourself (DRY) rule. Restructure your code:
const string path = #"C:\Users\Niyomal N\Desktop\Assignment\Assignment";
string[] files = new string[] { "D1_H1.txt", "D2_H1.txt", "D3_H1.txt", ... };
foreach (string file in files) {
string fullPath = Path.Combine(path, file);
//TODO: count words of file `fullPath`
}
Storing the word counts in an array is not optimal as you will have to traverse the array for each word you are encountering in a file.
Use a dictionary instead which has a constant lookup time. That's much faster.
var wordCount = new Dictionary<string, int>();
You can then count the words like this:
int count;
if (wordCount.TryGetValue(word, out count)) {
wordCount[word] = count + 1;
} else {
wordCount[word] = 1;
}
UPDATE
You can test for keywords like this
var keywords = new HashSet<string> { "Red", "Green", "Blue" };
string word = "Green";
if (keywords.Contains(word)) {
...
}
HasSets are as fast as dictionaries.
Be careful with the word casing. HashSets are case sensitive by default. If "red" and "Red" and "RED" have to be found alltogehter, initialize the HashSet like this:
var keywords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
{ "Red", "Green", "Blue" };
List<KeyValuePair<string, string>> completeList = new List<KeyValuePair<string, string>>();
completeList.AddRange("D1_H1.txt",File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D1_H1.txt").Split(' '));
completeList.AddRange("D1_H2.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D2_H1.txt").Split(' '));
completeList.AddRange("D1_H3.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D3_H2.txt").Split(' '));
completeList.AddRange("D1_H4.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D4_H2.txt").Split(' '));
completeList.AddRange("D1_H5.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D5_H2.txt").Split(' '));
completeList.AddRange("D1_H6.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\Given_doc.txt").Split(' '));
var result = completeList.GroupBy(r => r.Key).Select(r => new {File = r.Key, Red = r.Count(s => s.Value == "red"), Green = r.Count(s => s.Value == "green"), Blue = r.Count(s => s.Value == "blue") });
foreach (var itm in result)
{
Console.WriteLine(itm.File);
Console.WriteLine(itm.Red);
Console.WriteLine(itm.Green);
Console.WriteLine(itm.Blue);
}

How to use LINQ select something contain string[]

How to use linq to select something fit the conditions below,
I want select the words JUST contains the string in ArStr[], i.e. a,b,c
In the Wordslist, "aabb" don't contain "c", "aacc" don't contain "b", "aabbccd" contain "d".
So they are not the words I want.
Please help.
Wordslist :
aabb
aacc
aaabbcc
aabbbcc
aabbccd
ArStr[] :
"a"
"b"
"c"
Expected Query:
aaabbcc
aabbbcc
IEnumerable<Word> Query =
from Word in Wordslist
where
Word.Value.Contains(ArStr[0]) // 1
&& Word.Value.Contains(ArStr[1]) // 2
&& Word.Value.Contains(ArStr[2]) // 3
select Word;
You can construct a set of white-list characters and then filter those words that are set-equal with that white-list (ignoring duplicates and order).
var chars = new HashSet<char>(ArStr); // Construct white-list set
var query = from word in wordsList
where chars.SetEquals(word) // Word must be set-equal with white-list
select word;
or
var query = wordsList.Where(chars.SetEquals);
As you've probably noticed, the query you've written does return "aabbccd", because that string contain "a", it contains "b", and it contains "c".
Assuming that ArStr can only contain one-character strings, and you want to return strings that contain only the specified characters, so you should say (adapted from Ani's answer):
var chars = new HashSet<char>(ArStr.Select(s => s[0]));
var query = wordslist.Where(w => chars.SetEquals(w.Value));
However, if the ArStr elements could be more than one character long, the problem needs to be better defined, and the solution will be more complicated.
Use this method to evaluate a word if it passes your condition or not:
bool HasValidCharacters(string word)
{
var allowedCharacters = new List<string> { "a", "b", "c" };
return string.Join("", word.GroupBy(c => c)
.Select(g => g.Key)
.OrderBy(g => g))
.Equals(string.Join("", allowedCharacters.OrderBy(c => c)));
}
Then simply call the method to get the required list:
var words = new List<string> { "aabb", "aacc", "aaabbcc", "aabbbcc", "aabbccd" };
var matchingWords = words.Where(HasValidCharacters);
You could try this:
List<String> words = new List<string> { "aabb", "aacc", "aaabbcc", "aabbbcc", "aabbccd" };
List<string> allowed = new List<string> { "a", "b", "c" };
var lst = words.Where(word => allowed.All(a => word.Contains(a) && !Regex.IsMatch(word, "[^" + string.Join("", allowed) + "]"))).ToList();
Just another way to implement it.
I think you can use String.Trim Method (Char()) on each element , then the empty element is you want .
var arr = new string[] { "aabb", "aacc", "aaabbcc", "aabbbcc", "aabbccd" };
var arStr = new string[] { "a", "b", "c" };
var str = string.Join("", arStr);
var result = from p in arr
let arCharL = arStr.Select(a => Convert.ToChar(a)).ToArray()
let arCharR = p.ToCharArray()
where p.Trim(arCharL).Length == 0 && str.Trim(arCharR).Length == 0
select p;

Categories

Resources