I have 2 Lists, Planets and Favorites. They contain multiple words separated by spaces.
I split the Lists by space into Sublists.
Now I want to check if Planets contains a Name from Favorites.
But Planets.Contains() does not find a match.
http://rextester.com/YLOG10363
// Planets List
//
List<string> Planets = new List<string>();
Planets.Add("First Mercury Gray");
Planets.Add("Second Venus Yellow");
Planets.Add("Third Earth Blue");
Planets.Add("Fourth Mars Red");
// Favorites List
//
List<string> Favorites = new List<string>();
Favorites.Add("Venus Hot");
Favorites.Add("Mars Cold");
// Sublists
//
string[] arrPlanets = null;
string[] arrFavorites = null;
List<string> Order = new List<string>();
List<string> Names = new List<string>();
List<string> Colors = new List<string>();
// In each Line of Planets & Favorites Lists, Split by Space
// Add Word to it's Sublist
//
for (int i = 0; i < Planets.Count; i++)
{
// Create Planet Sublists
arrPlanets = Convert.ToString(Planets[i]).Split(' ');
Order.Add(arrPlanets[0]);
Names.Add(arrPlanets[1]);
Colors.Add(arrPlanets[2]);
// Create Favorites Sublist
// Prevent Favorites index from going out of range
if (i < Favorites.Count())
{
arrFavorites = Convert.ToString(Favorites[i]).Split(' ');
// Display Message if Planets List Contains a Name from Favorites
//
if (Planets.Contains(arrFavorites[0]))
{
Console.WriteLine("Favorite Detected.");
}
}
}
OK, I've looked at all the answers, and you might want to look at this possibility as well, since it represents the smallest amount of refactoring to your code:
Replace
if (Planets.Contains(arrFavorites[0]))
With
if (Planets.Any(p => p.Contains(arrFavorites[0])))
Not the most performant, since there are better algorithms to do checks for matching terms. But looking at your code, it doesn't seem like the most important thing that you're after. So possibly, my approach might make sense, then.
Hope that helps.
It really isn't very clear what you are trying to accomplish, but since you said you want to find out if Planets contains any name from Favorites, and assuming the name is always the first word in each favorite,
var PlanetsHasFavorite = Planets.Any(p => Favorites.Select(f => f.Split(' ')[0]).Any(f => p.Split(' ').Contains(f)));
PlanetsHasFavorite will be true if any Planet matches a name from Favorites.
Assuming you meant you actually want to get a list of the matching planets,
var PlanetsAreFavorite = Planets.Where(p => Favorites.Select(f => f.Split(' ')[0]).Any(f => p.Split(' ').Contains(f))).ToList();
If you want to see if any word in any Favorite is contained in any Planet, then you only need to split the Favorites into words, and then see if any planet contains any word.
So, to get all the Favorite words, we can do something like this:
var favoriteWords = Favorites.SelectMany(i => i.Split(' '));
Now, we can loop through all the planets and see if we have any matches:
Planets.ForEach(p =>
{
if (favoriteWords.Any(p.Contains))
{
Console.WriteLine($"One of your favorite planets is: {p}");
}
});
And the result is:
Or, if you just wanted to show the favorite words that were matched, you could do something like:
Console.WriteLine("These favorite words were matched: ");
Planets.ForEach(p => favoriteWords.Where(p.Contains).ToList().ForEach(Console.WriteLine));
Don't use arrays if you don't know the number of items you want them to hold and DON'T knowingly set variables to null.
Use Lists instead:
List<string> planets = new List<string>();
List<string> favorites = new List<string>();
That being said your code is totally wrong.
What you are trying to achieve is something like this:
List<string> Planets = new List<string>();
Planets.Add("First Mercury Gray");
Planets.Add("Second Venus Yellow");
Planets.Add("Third Earth Blue");
Planets.Add("Fourth Mars Red");
List<string> Favorites = new List<string>();
Favorites.Add("Venus Hot");
Favorites.Add("Mars Cold");
// Unless you need favorites to hold tokens seperated by a white space
// you shouldn't make another list such as this one:
List<string> faveKeywords = Favorites.SelectMany(fave => fave.Split(' ')).ToList();
foreach (var token in from line in Planets from token in line.Split(' ') where faveKeywords.Contains(token) select token)
{
Console.WriteLine($"Favorite detected: {token}" );
}
Or if you keep insisting on making that Order, Color, Name:
foreach (var tokens in Planets.Select(str => str.Split(' ')))
{
Order.Add(tokens[0]);
Names.Add(tokens[1]);
Colors.Add(tokens[2]);
foreach (var token in tokens.Where(token => faveKeywords.Contains(token)))
{
Console.WriteLine($"Favorite detected: {token}");
}
}
You need to learn from examples such as this one and observe more than you ask.
Related
I'm trying to write a lambda expression to compare members of a list to a string, but I also need to catch elements in the list that might have their letter scrambled up.
Here's code I got right now
List<string> listOfWords = new List<String>() { "abc", "test", "teest", "tset"};
var word = "test";
var results = listOfWords.Where(s => s == word);
foreach (var i in results)
{
Console.Write(i);
}
So this code will find string "test" in the list and will print it out, but I also want it to catch cases like "tset". Is this possible to do easily with linq or do I have to use loops?
How about sorting the letters and seeing if the resulting sorted sequences of chars are equal?
var wordSorted = word.OrderBy(c=>c);
listOfWords.Where(w => w.OrderBy(c=>c).SequenceEqual(wordSorted));
I want to merge two lists together, but the methods I've found add the list's populants to new lines, like this.
parts on list one,
parts on list one,
parts on list one,
parts on list two,
parts on list two,
parts on list two,
However, I want it to look like this.
parts on list one,parts on list two
parts on list one,parts on list two
parts on list one,parts on list two
This is what I have to merge them them (+ the lists themselves so far)
static void Main(string[] args)
{
iterateRoutes(#"C:\Users\peepee poopoo\gtfs\routes.txt", #"C:\Users\peepee poopoo\gtfs\calendar.txt");
//log.ForEach(Console.WriteLine);
}
public static List<string> log = new List<string>();
public static List<string> datesStd = new List<string>();
public static List<string> datesEnd = new List<string>();
public static void iterateRoutes(string rtname, string std)
{
foreach (var route in File.ReadAllLines(rtname)) // iterate file lines
{
var elements = route.Split(','); // split current line into elements
log.Add(elements[0] + "," + elements[2] + "," + "2021426,20991231,"); // add first & third element to log
}
foreach (var route in File.ReadAllLines(std)) // iterate file lines
{
//var elements = route.Split(','); // split current line into elements
//log.Add("," + elements[8]); // add first & third element to log
}
//datesStd.ForEach(Console.WriteLine);
// datesEnd.ForEach(Console.WriteLine);
log.AddRange(datesStd);
log.ForEach(Console.WriteLine);
}
How could I do this?
To join the files you can use zip:
IEnumerable<string> fileOne = File.ReadLines("one.txt");
IEnumerable<string> fileTwo = File.ReadLines("two.txt");
// For each line in fileOne, take a line from fileTwo
// call these lines l1 and l2 respectively, and produce
// a merged result.
IEnumerable<string> joined = fileOne.Zip(fileTwo, (l1, l2) => $"{l1}{l2}");
// write the result to a file
File.WriteAllLines("out.txt", joined);
// or materialize it to a list
List<string> lines = joined.ToList();
Zip documentation
So if we have one file:
A
B
C
And another file:
D
E
F
Then the result will be
AD
BE
CF
Try it online. Note that I've replaced File.ReadLines with my own fake version here, since the site I'm using doesn't support reading files.
I have a list of strings, which can be considered 'filters'.
For example:
List<string> filters = new List<string>();
filters.Add("Apple");
filters.Add("Orange");
filters.Add("Banana");
I have another list of strings, which contains sentences.
Example:
List<string> msgList = new List<string>();
msgList.Add("This sentence contains the word Apple.");
msgList.Add("This doesn't contain any fruits.");
msgList.Add("This does. It's a banana.");
Now I want to find out which items in msgList contains a fruit. For which, I use the following code:
foreach(string msg in msgList)
{
if(filters.Any(msg.Contains))
{
// Do something.
}
}
I'm wondering, is there a way in Linq where I can use something similar to List.Any() where I can check if msgList contains a fruit, and if it does, also get the fruit which matched the inquiry. If I can get the matching index in 'filters' that should be fine. That is, for the first iteration of the loop it should return 0 (index of 'Apple'), for the second iteration return null or something like a negative value, for the third iteration it should return 2 (index of 'Banana').
I checked around in SO as well as Google but couldn't find exactly what I'm looking for.
You want FirstOrDefault instead of Any.
FirstOrDefault will return the first object that matches, if found, or the default value (usually null) if not found.
You could use the List<T>.Find method:
foreach (string msg in msgList)
{
var fruit = filters.Find(msg.Contains);
if (fruit != null)
{
// Do something.
}
}
List<string> filters = new List<string>() { "Apple", "Orange", "Banana" };
string msg = "This sentence contains the word Apple.";
var fruit = Regex.Matches(msg, #"\w+", RegexOptions.IgnoreCase)
.Cast<Match>()
.Select(x=>x.Value)
.FirstOrDefault(s => filters.Contains(s));
A possible approach to return the indexes of the elements
foreach (string msg in msgList)
{
var found = filters.Select((x, i) => new {Key = x, Idx = i})
.FirstOrDefault(x => msg.Contains(x.Key));
Console.WriteLine(found?.Idx);
}
Note also that Contains is case sensitive, so the banana string is not matched against the Banana one. If you want a case insensitive you could use IndexOf with the StringComparison operator
So basically I have this loop where each sentence in processedSentencesList gets iterated and scanned for words which exist in the list entityString. And each entityString found in each sentence is added to var valid_words.
But the entities "Harry Potter" and "Ford Car" does not get added because of the 'sentence.Split()' statement.
How do I alter this code so that existing entities with spaces do not get separated in to two words?
List <string> entityString = new List<string>();
entityString.Add("Harry Potter"); //A name which i do not want to split
entityString.Add("Ford Car"); //A name which i do not want to split
entityString.Add("Broom");
entityString.Add("Ronald");
List <string> processedSentencesList = new List<string>();
processedSentencesList.Add("Harry Potter is a wizard");
processedSentencesList.Add("Ronald had a Broom and a Ford Car");
foreach (string sentence in processedSentencesList)
{
var words = sentence.Split(" ".ToCharArray());
//But it splits the names as well
var valid_words = words.Where(w =>
entityStrings.Any(en_li => en_li.Equals(w)));
//And therefore my names do not get added to the valid_words list
}
When printed, Output I get right now:
Broom
Ronald
Output I expect:
Harry Potter
Ford Car
Broom
Ronald
Basically, the entities with spaces in between (2 or more words) gets separated and thus cannot be matched to existing entities. How do I fix this?
Change your foreach with this :
List<String> valid_words = new List<String>();
foreach (string sentence in processedSentencesList)
{
valid_words.AddRange(entityString.Where(en_li => sentence.Contains(en_li)));
}
valid_words = valid_words.Distinct().ToList();
You could try matching instead of splitting.
[A-Z]\S+(?:\s+[A-Z]\S+)?
DEMO
You could loop through each item and use the 'String.Contains()' method, which will prevent you from having to split your search strings.
Example:
List<string> valid_words = new List<string>();
foreach (string sentence in processedSentencesList)
{
foreach (string entity in entityString)
{
if (sentence.Contains(entity))
{
valid_words.Add(entity);
}
}
}
I have a List<string> with some 10 strings.
The values are as follows:
\\Server\Site\MySite\File1.xml
\\Server\Site\MySite\File2.xml
\\Server\Site\MySite\File2.xml
.......................
\\Server\Site\MySite\File10.xml
I need to extract \MySIte\File1.xml to \MySite\File10.xml and store in another list.
I tried to use Split keyword, with another list to populate the splitted string. But it doesn't seem to give the correct answer.
Below is the code:
for(int index=0;index<list.Count;list++)
{
string[] myArray=list[index].Split('\\');
for(int innerIndex=0;innerIndex<myArray.Length;innerIndex++)
{
anotherList[innerIndex]=myArray[2]+"\\"+myArray[3];
}
}
Experts please help.
You don't need to work too hard if you know the input of all the strings
str.Substring(str.IndexOf("\\MySite"))
One word: LINQ!
var results = (from x in source
let parts = x.Split('\\')
select String.Join("\\", parts.Skip(1)).ToArray();
You can use following code.
List<string> source = new List<string>();
source.Add(#"\\Server\Site\MySite\File1.xml");
source.Add(#"\\Server\Site\MySite\File2.xml");
source.Add(#"\\Server\Site\MySite\File2.xml");
source.Add(#"\\Server\Site\MySite\File10.xml");
foreach(string s in source)
{
string[] parts = s.Split(new string[]{ Path.DirectorySeparatorChar.ToString() },StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine(parts[parts.Length - 1] + Path.DirectorySeparatorChar +
parts[parts.Length - 2]);
}
I would just remove anything before \MySite and get the rest:
Test data used:
List<string> source = new List<string>
{
#"\\Server\Site\MySite\File1.xml",
#"\\Server\Site\MySite\File2.xml",
#"\\Server\Site\MySite\File2.xml",
#"\\Server\Site\MySite\File10.xml",
};
query:
var result =
source
// Start removing at 0 and remove everything before '\MySite'
.Select(x => x.Remove(0, x.IndexOf("\\MySite")))
.ToList();