using System;
using System.Linq;
public class Program
{
public static void Main()
{
var flame = new string[]
{
"bad", "word"
}
;
var text = "this contains some bad words";
foreach (string item in text.Split(' '))
{
bool testerino = flame.Any(item.Contains);
if (testerino)
{
Console.WriteLine("1");
}
}
}
}
https://dotnetfiddle.net/Widget/as5iTs
I want Console.WriteLine("1"); to run only once. I tried to use First() and FirstOrDefault() but I was not able to use it without syntax errors. Why I'm using a Split? I don't know. It was the only way to get .Contains() running. I did receive errors using char item in text with Contains().
I don't need to use foreach or even First() it is just only way I know so far.
Any help is very much appreciated.
You want something like this:
var anyFlameWords =
text
.Split(' ')
.Any(word => flame.Contains(word));
if (anyFlameWords)
Console.WriteLine("1");
You don't need First/FirstOrDefault, unless you want the first element from the collection, which looking at your existing code is not what you require.
You can break after you act on the first match.
foreach (string item in text.Split(' '))
{
bool testerino = flame.Any(item.Contains);
if (testerino)
{
Console.WriteLine("1");
break;
}
}
But a more concise alternative is just
if (test.Split(' ').Any(f=>flame.Contains(f))) Console.WriteLine("1");
If you are interested to see how IEnumerable<T>.FirstOrDefault could be used here, note that you can pass in a predicate to FirstOrDefault to get the first item in the IEnumerable<T> that matches that predicate (or the default value for T if nothing matches):
var firstMatch = test.Split(' ').FirstOrDefault(w=>flame.Contains(w));
if (firstMatch != null) Console.WriteLine("1");
Just add a break; to your if-case:
foreach (string item in text.Split(' '))
{
bool testerino = flame.Any(item.Contains);
if (testerino)
{
Console.WriteLine("1");
break;
}
}
A break causes to exit the foreach-loop immediately.
This linq may help
bool badWordExists = text.Split(' ').Any(s => flame.Contains(s));
if(badWordExists) Console.WriteLine("1");
Related
foreach (Objecta a in aList())
{
foreach (Objectb b in bList)
{
if (a.variable != b.variable1 && a.variable() != b.variable2)
{
a.setVariable("Error");
}
}
}
The problem I am getting is that it goes through the foreach loop the first time and it sets variable to error without checking if other values (when it goes through the loop again) finds a match.
What I would like is to wait until it goes through all the lists and at the last foreach loop iteration if nothing in aList matches the variable target && variable source in bList then finally set it to Error flag.
Any suggestions to get around this will be appreciated.
Try doing it the other way around. Search for a match instead of searching for non-matches.
foreach (Objecta a in aList())
{
bool foundMatch = false;
foreach (Objectb b in bList)
{
if (a.variable == b.variable1 || a.variable() == b.variable2)
{
foundMatch = true;
break;
}
}
if (!foundMatch)
{
a.setVariable("Error");
}
}
I think this is what you are looking for. So if StoreList is the outer loop and LinkList is the inner loop. You want to search all the links to see if there's an ID that matches the store ID. If you find a match, stop searching the links. After the search through the links, set an error on the store if there was no match, then go to the next store.
foreach (Objecta a in aList())
{
var foundMatch = false;
foreach (Objectb b in bList)
{
if (a.variable == b.variable1 || a.variable() == b.variable2)
{
fondMatch = true;
break;
}
}
if (!foundMatch) a.setVariable("Error");
}
I think you want something like this:
First select all the item values from aList and bList and put them in a seperate array:
var aVals = aList.Select(x=>x.value1).ToArray();
var bListVals1 = bItems.Select(x=>x.value1).ToArray();
var bListVals2 = bItems.Select(x=>x.value2).ToArray();
var bVals = bListVals1.Concat(bListVals2);
Then, get the values both lists have in common:
var correctVals = bVals.Intersect(aVals);
These are the correct values and so all the other values are wrong:
var wrongVals = aVals.Except(correctVals);
Now you have the values that are wrong and can act accordingly:
wrongAItems = aList.Where(a => wrongVals.Contains(a.value));
foreach(wrongA in wrongAItems){
wrongA.setVariable("Error");
}
foreach (Store s in processFlowStores.getStoresList())
{
if (!processFlowLinks.Any(l => s.getNodeId() == l.getLinkSource() ||
s.getNodeId() == l.getLinkTarget()))
{
s.setID("Error: FailedOperation Error - 123.123.121");
}
}
EDIT: more compact solution using Linq. Basically, if none of the links has it as either source or target, mark it as error.
In the code below, .Contains() only return string that start with the text I type in the TextBox. I want it to return all records that contain that string anywhere in the the searched field. Please advise how I can get Contains() to return the value, alternate ways are welcome as well
Thanks
using (var GC = new GroundCommanderEntities())
{
foreach (var Current in GC.IMF_Extensions.Where(filter => filter.Description.Contains(Search_txt.Text) ))
{
string sss = Current.Description;
Coll.Add(sss);
}
// tried same result foreach (var Current in GC.IMF_Extensions.Where(filter => filter.Description.Contains(Search_txt.Text.Trim()) || filter.Description.StartsWith(Search_txt.Text) || filter.Description.EndsWith(Search_txt.Text)))
// tried same result foreach (var Current in GC.IMF_Extensions.Where(filter => filter.Description.Contains(Search_txt.Text.Trim()) ))
}
Try a simpler method without Linq:
using (var GC = new GroundCommanderEntities())
{
foreach (var Current in GC.IMF_Extensions)
{
if (Current.Description.Contains(Search_txt.Text))
{
Coll.Add(Current.Description);
}
}
}
Try to use ToLower for both strings before Contains. It should be work.
using (var GC = new GroundCommanderEntities())
{
foreach (var Current in GC.IMF_Extensions.Where(filter => filter.Description.ToLower().Contains(Search_txt.Text.ToLower())))
{
Coll.Add(Current.Description);
}
}
I am trying to minimize this piece of code
public static void UnfavSong(Song song)
{
List<string> favorites = FileManagement.GetFileContent_List(FAVS_FILENAME);
foreach (string s in favorites)
{
Song deser = SongSerializer.Deserialize(s);
if (deser.ID == song.ID)
{
favorites.Remove(s);
break;
}
}
FileManagement.SaveFile(FAVS_FILENAME, favorites);
}
But I feel like the whole foreach part can be made much shorter.
Is there a way in C# to cut this down to the core?
Using LINQ
favorites.RemoveAll(s => SongSerializer.Deserialize(s).ID == song.ID)
Btw. your code shouldn't work at all as you can't modify the List during it's iteration
you can use linq Where() to filter them:
List<string> result = favorites.Where(x=>SongSerializer.Deserialize(x).ID != song.ID).ToList();
This will give you all element except with the matching ID with song.ID
I am somewhat struggling with the terminology and complexity of my explanations here, feel free to edit it.
I have 1.000 - 20.000 objects. Each one can contain several name words (first, second, middle, last, title...) and normalized numbers(home, business...), email adresses or even physical adresses and spouse names.
I want to implement a search that enables users to freely combine word parts and number parts.When I search for "LL 676" I want to find all objects that contain any String with "LL" AND "676".
Currently I am iterating over every object and every objects property, split the searchString on " " and do a stringInstance.Contains(searchword).
This is too slow, so I am looking for a better solution.
What is the appropriate language agnostic data structure for this?
In my case I need it for C#.
Is the following data structure a good solution?
It's based on a HashMap/Dictionary.
At first I create a String that contains all name parts and phone numbers I want to look through, one example would be: "William Bill Henry Gates III 3. +436760000 billgatesstreet 12":
Then I split on " " and for every word x I create all possible substrings y that fullfill x.contains(y). I put every of those substrings inside the hashmap/dictionary.
On lookup/search I just need to call the search for every searchword and the join the results. Naturally, the lookup speed is blazingly fast (native Hashmap/Dictionary speed).
EDIT: Inserts are very fast as well (insignificant time) now that I use a smarter algorithm to get the substrings.
It's possible I've misunderstood your algorithm or requirement, but this seems like it could be a potential performance improvement:
foreach (string arg in searchWords)
{
if (String.IsNullOrEmpty(arg))
continue;
tempList = new List<T>();
if (dictionary.ContainsKey(arg))
foreach (T obj in dictionary[arg])
if (list.Contains(obj))
tempList.Add(obj);
list = new List<T>(tempList);
}
The idea is that you do the first search word separately before this, and only put all the subsequent words into the searchWords list.
That should allow you to remove your final foreach loop entirely. Results only stay in your list as long as they keep matching every searchWord, rather than initially having to pile everything that matches a single word in then filter them back out at the end.
In case anyone cares for my solution:
Disclaimer:
This is only a rough draft.
I have only done some synthetic testing and I have written a lot of it without testing it again.I have revised my code: Inserts are now ((n^2)/2)+(n/2) instead of 2^n-1 which is infinitely faster. Word length is now irrelevant.
namespace MegaHash
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class GenericConcurrentMegaHash<T>
{
// After doing a bulk add, call AwaitAll() to ensure all data was added!
private ConcurrentBag<Task> bag = new ConcurrentBag<Task>();
private ConcurrentDictionary<string, List<T>> dictionary = new ConcurrentDictionary<string, List<T>>();
// consider changing this to include for example '-'
public char[] splitChars;
public GenericConcurrentMegaHash()
: this(new char[] { ' ' })
{
}
public GenericConcurrentMegaHash(char[] splitChars)
{
this.splitChars = splitChars;
}
public void Add(string keyWords, T o)
{
keyWords = keyWords.ToUpper();
foreach (string keyWord in keyWords.Split(splitChars))
{
if (keyWord == null || keyWord.Length < 1)
return;
this.bag.Add(Task.Factory.StartNew(() => { AddInternal(keyWord, o); }));
}
}
public void AwaitAll()
{
lock (this.bag)
{
foreach (Task t in bag)
t.Wait();
this.bag = new ConcurrentBag<Task>();
}
}
private void AddInternal(string key, T y)
{
for (int i = 0; i < key.Length; i++)
{
for (int i2 = 0; i2 < i + 1; i2++)
{
string desire = key.Substring(i2, key.Length - i);
if (dictionary.ContainsKey(desire))
{
List<T> l = dictionary[desire];
lock (l)
{
try
{
if (!l.Contains(y))
l.Add(y);
}
catch (Exception ex)
{
ex.ToString();
}
}
}
else
{
List<T> l = new List<T>();
l.Add(y);
dictionary[desire] = l;
}
}
}
}
public IList<T> FulltextSearch(string searchString)
{
searchString = searchString.ToUpper();
List<T> list = new List<T>();
string[] searchWords = searchString.Split(splitChars);
foreach (string arg in searchWords)
{
if (arg == null || arg.Length < 1)
continue;
if (dictionary.ContainsKey(arg))
foreach (T obj in dictionary[arg])
if (!list.Contains(obj))
list.Add(obj);
}
List<T> returnList = new List<T>();
foreach (T o in list)
{
foreach (string arg in searchWords)
if (dictionary[arg] == null || !dictionary[arg].Contains(o))
goto BREAK;
returnList.Add(o);
BREAK:
continue;
}
return returnList;
}
}
}
I have a dictionary with a list of strings that each look something like:
"beginning|middle|middle2|end"
Now what I wanted was to do this:
List<string> stringsWithPipes = new List<string>();
stringWithPipes.Add("beginning|middle|middle2|end");
...
if(stringWithPipes.Contains("beginning|middle|middle2|end")
{
return true;
}
problem is, the string i'm comparing it against is built slightly different so it ends up being more like:
if(stringWithPipes.Contains(beginning|middle2|middle||end)
{
return true;
}
and obviously this ends up being false. However, I want to consider it true, since its only the order that is different.
What can I do?
You can split your string on | and then split the string to be compared, and then use Enumerable.Except along with Enumerable.Any like
List<string> stringsWithPipes = new List<string>();
stringsWithPipes.Add("beginning|middle|middle2|end");
stringsWithPipes.Add("beginning|middle|middle3|end");
stringsWithPipes.Add("beginning|middle2|middle|end");
var array = stringsWithPipes.Select(r => r.Split('|')).ToArray();
string str = "beginning|middle2|middle|end";
var compareArray = str.Split('|');
foreach (var subArray in array)
{
if (!subArray.Except(compareArray).Any())
{
//Exists
Console.WriteLine("Item exists");
break;
}
}
This can surely be optimized, but the above is one way to do it.
Try this instead::
if(stringWithPipes.Any(P => P.split('|')
.All(K => "beginning|middle2|middle|end".split('|')
.contains(K)))
Hope this will help !!
You need to split on a delimeter:
var searchString = "beginning|middle|middle2|end";
var searchList = searchString.Split('|');
var stringsWithPipes = new List<string>();
stringsWithPipes.Add("beginning|middle|middle2|end");
...
return stringsWithPipes.Select(x => x.Split('|')).Any(x => Match(searchList,x));
Then you can implement match in multiple ways
First up must contain all the search phrases but could include others.
bool Match(string[] search, string[] match) {
return search.All(x => match.Contains(x));
}
Or must be all the search phrases cannot include others.
bool Match(string[] search, string[] match) {
return search.All(x => match.Contains(x)) && search.Length == match.Length;
}
That should work.
List<string> stringsWithPipes = new List<string>();
stringsWithPipes.Add("beginning|middle|middle2|end");
string[] stringToVerifyWith = "beginning|middle2|middle||end".Split(new[] { '|' },
StringSplitOptions.RemoveEmptyEntries);
if (stringsWithPipes.Any(s => !s.Split('|').Except(stringToVerifyWith).Any()))
{
return true;
}
The Split will remove any empty entries created by the doubles |. You then check what's left if you remove every common element with the Except method. If there's nothing left (the ! [...] .Any(), .Count() == 0 would be valid too), they both contain the same elements.