Linq search not returning all entries - c#

Guys i have two problems... firstly i have a linq query search that queries name and surname and returns the results from the db. eg .if i search John it will return John travolta which is perfect (my problem being) if i search john travolta the name and surname together it will find no result?... help.
Problem number 2: how do i clear the results of the search when i search again? because when i search the second time the results of the previous search is
public class SearchResults
{
public string Name { get; set; }
public string Surname { get; set; }
public SearchResults(string name, string surname)
{
Name = name;
Surname = surname;
}
protected void btnSearch_Click(object sender, EventArgs e)
{
string search = txtSearch.Text.ToLower().TrimEnd();
lsSearchResults = null;
lsSearchResults = new List<SearchResults>();
Repeater1.DataSource = null;
lsSearchResults.Clear();
Repeater1.DataSource = "";
}
if (string.IsNullOrWhiteSpace(txtSearch.Text))
{
lblResults.Text = "Please enter a password & Em#ilAddress";
lsSearchResults.Clear();
return;
}
else
{
var found = (from User in myDB.Memberships
where User.Name.ToLower().Contains(txtSearch.Text.ToLower()) ||
User.Surname.ToLower().Contains(txtSearch.Text.ToLower())
select new { User.Name, User.Surname });
// validates items in search query if Exist
if (!ChillZARdev.App_Code.Utilities.IsEmpty(found))
{
foreach (var user in found)
{
lsSearchResults.Add(new SearchResults(user.Name, user.Surname));
// ls.Add(user.Name + " " + user.Surname);
// Response.Write(user);
}
Repeater1.DataSource = lsSearchResults;
Repeater1.DataBind();
}
}

Short solution would be to include firstname and surname together in the where clause:
var found = (from User in myDB.Memberships
where User.Name.ToLower().Contains(txtSearch.Text.ToLower()) ||
User.Surname.ToLower().Contains(txtSearch.Text.ToLower()) ||
(User.Name + " " + User.Surname).ToLower().Contains(txtSearch.Text.ToLower())
However, this means a search for "Travolta john" wouldn't return any results.
If this matters you should split up the string by spaces, and search for each word.
List<string> searchSplit = txtSearch.Text.ToLower().Split(' ');
var found = (from User in myDB.Memberships
where IsMatch(searchSplit, User)
private bool IsMatch(List<string> searchSplit, User User){
return searchSplit.Count() ==
searchSplit.Where(x => User.Name.ToLower().Contains(x) ||
User.Surname.ToLower().Contains(x)).Count();
}
Ideally, as Steve B has commented, it would be best to implement a pre-built search engine which would require more tools for searching than a simple custom search would. But it depends on what your budget/requirements are.

var found = myDB.Memberships;
foreach(var word in txtSearch.Text)
{
var word2=word; //Must do this because word won't be right at execution
found=found.Where(u=>u.Name.Contains(word2) || u.Surname.Contains(word2));
}
I've removed the .ToLower, because the columns should already be marked as being case insensitive, and will be unless you've either changed it directly, or the default culture of the database.

A couple of months back, i was having the same problem, i was using singleton design pattern and the root problem was with the cache.. and for both of your problems there is one solution - you can try to clean the cache of a LINQ to SQL DataContext and you can do that by following way
public static void ClearCache(this DataContext context)
{
const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var method = context.GetType().GetMethod("ClearCache", FLAGS);
method.Invoke(context, null);
}
and you can call that method
public void DoWork()
{
//do work...
dataContext.ClearCache();
//do more work...
}
Hope it helps

Related

Dynamic linq filter children using pivot

I have business objects that look like the following:
class Project
{
public int ID
{
get;set;
}
public string ProjectName
{
get;set;
}
public IList<ProjectTag> ProjectTags
{
get;set;
}
}
class ProjectTag
{
public int ID
{
get;set;
}
public int ProjectID
{
get;set;
}
public string Name
{
get;set;
}
public string Value
{
get;set;
}
}
Example Data:
Project:
ID ProjectName
1 MyProject
ProjectTags:
ID ProjectID Name Value
1 1 Name 1 Value 1
2 1 Name 2 Value 2
3 1 Name 3 Value 3
Basically it's a way for our users to define their own columns on the Project. As a result, it's important to remember that I don't know the names of the ProjectTag entries at design time.
What I'm trying to accomplish is to give our users the ability to select projects based on search criteria using System.Linq.Dynamic. For instance, to select just the project in my example above, our users could enter this:
ProjectName == "MyProject"
The more complicated aspect is applying a filter to the ProjectTags. Our application currently allow users to do this in order to filter Projects by their ProjectTags:
ProjectTags.Any(Name == "Name 1" and Value == "Value 1")
That works, but starts to get a bit messy for end users to use. Ideally I'd like to write something that would let them do the following:
Name 1 == "Value 1"
Or if necessary (due to white space in the name), something like the following...
[Name 1] == "Value 1"
"Name 1" == "Value 1"
For lack of a better explanation, it seems like I want to do the equivalent of a SQL pivot on the ProjectTags, and then still be able to execute a where clause against that. I've looked at some of the questions on StackOverflow about pivots and dynamic pivoting, but I haven't found anything too useful.
I've also been thinking about looping through all the ProjectTag Names and building a dynamic query using a left join on each. I guess something like this:
select
Project.*,
Name1Table.Value [Name 1],
Name2Table.Value [Name 2],
Name3Table.Value [Name 3]
from
Project
left join ProjectTag Name1Table on Name = 'Name 1'
left join ProjectTag Name2Table on Name = 'Name 2'
left join ProjectTag Name3Table on Name = 'Name 3'
And then take that query and apply a where clause to it. But I'm not really sure how to do that in Linq as well as dealing with the white space in the name.
I also came across ExpandoObject. I thought possibly I could convert Project to an ExpandoObject. Then loop through all known ProjectTag names, adding each name to the ExpandoObject and, if that Project had a ProjectTag for that name, use that ProjectTag value as the value, else empty string. For example...
private static object Expand(
Project project,
List<string> projectTagNames)
{
var expando = new ExpandoObject();
var dictionary = (IDictionary<string, object>) expando;
foreach (var property in project.GetType()
.GetProperties())
{
dictionary.Add(property.Name, property.GetValue(project));
}
foreach (var tagName in projectTagNames)
{
var tagValue = project.ProjectTags.SingleOrDefault(p => p.Name.Equals(tagName));
dictionary.Add(tagName, tagValue?.Value ?? "");
}
return expando;
}
The exciting thing about this solution is I have an object that looks exactly like I think it should prior to filtering with a where clause. It even seems to accommodate spaces in the property name.
Then of course I found out that dynamic linq doesn't work nicely with ExpandoObject, and so it can't find the dynamic properties. I guess that's because it essentially has a type of Object which isn't going to define any of the dynamic properties. Maybe it's possible to generate a type at run time that matches? Even if that works, I don't think it can account for spaces in the Name.
Am I trying to accomplish too much with this functionality? Should I just tell the users to use syntax like ProjectTags.Any(Name == "Name1" and Value == "Value1")? Or is there some way to trick dynamic linq into understanding ExpandoObject? Seems like having a way to override the way dynamic linq resolves property names would be very handy.
How about using a translator to convert tag references?
I assume that tag names containing spaces will be surrounded by brackets ([]) and that Project field names are a known list.
public static class TagTranslator {
public static string Replace(this string s, Regex re, string news) => re.Replace(s, news);
public static string Surround(this string src, string beforeandafter) => $"{beforeandafter}{src}{beforeandafter}";
public static string SurroundIfMissing(this string src, string beforeandafter) => (src.StartsWith(beforeandafter) && src.EndsWith(beforeandafter)) ? src : src.Surround(beforeandafter);
public static string Translate(string q) {
var projectFields = new[] { "ID", "ProjectName", "ProjectTags" }.ToHashSet();
var opREStr = #"(?<op>==|!=|<>|<=|>=|<|>)";
var revOps = new[] {
new { Fwd = "==", Rev = "==" },
new { Fwd = "!=", Rev = "!=" },
new { Fwd = "<>", Rev = "<>" },
new { Fwd = "<=", Rev = ">=" },
new { Fwd = ">=", Rev = "<=" },
new { Fwd = "<", Rev = ">" },
new { Fwd = ">", Rev = "<" }
}.ToDictionary(p => p.Fwd, p => p.Rev);
var openRE = new Regex(#"^\[", RegexOptions.Compiled);
var closeRE = new Regex(#"\]$", RegexOptions.Compiled);
var termREStr = #"""[^""]+""|(?:\w|\.)+|\[[^]]+\]";
var term1REStr = $"(?<term1>{termREStr})";
var term2REStr = $"(?<term2>{termREStr})";
var wsREStr = #"\s?";
var exprRE = new Regex($"{term1REStr}{wsREStr}{opREStr}{wsREStr}{term2REStr}", RegexOptions.Compiled);
var tq = exprRE.Replace(q, m => {
var term1 = m.Groups["term1"].Captures[0].Value.Replace(openRE, "").Replace(closeRE, "");
var term1q = term1.SurroundIfMissing("\"");
var term2 = m.Groups["term2"].Captures[0].Value.Replace(openRE, "").Replace(closeRE, "");
var term2q = term2.SurroundIfMissing("\"");
var op = m.Groups["op"].Captures[0].Value;
if (!projectFields.Contains(term1) && !term1.StartsWith("\"")) { // term1 is Name, term2 is Value
return $"ProjectTags.Any(Name == {term1q} && Value {op} {term2})";
}
else if (!projectFields.Contains(term2) && !term2.StartsWith("\"")) { // term2 is Name, term1 is Value
return $"ProjectTags.Any(Name == {term2q} && Value {revOps[op]} {term1})";
}
else
return m.Value;
});
return tq;
}
}
Now you just translate your query:
var q = "ProjectName == \"Project1\" && [Name 1] == \"Value 1\" && [Name 3] == \"Value 3\"";
var tq = TagTranslator.Translate(q);

Two Groups with the same sAMAccountName, using FindOne() to getting the second occurance of group

I have a user that i'm currently listing "memberOf". I wanted to get some details about each group the user is a member of, such as distinguishedName, last modified, and description... The problem is, I'm using FindOne() in my code and i have a couple groups with sAMAccountName that are duplicated in various domains. Is there a way to use FindOne() and get the second occurance of the group as I have it coded below, or do I need to rewrite and use FindAll() and handling it that way.
Relevant Code Below:
foreach (object item in groups)
{
string groupProp = string.Empty;
using (DirectoryEntry dirEntry = CreateDirectoryEntry())
{
using (DirectorySearcher dirSearcher2 = new DirectorySearcher(dirEntry))
{
dirSearcher2.Filter = string.Format("(sAMAccountName=" + item + ")");
dirSearcher2.PropertiesToLoad.Add("description");
dirSearcher2.PropertiesToLoad.Add("whenChanged");
dirSearcher2.PropertiesToLoad.Add("distinguishedName");
SearchResult searchResult2 = dirSearcher2.FindOne();
if (searchResult2 != null)
{
DirectoryEntry employee = searchResult2.GetDirectoryEntry();
string desc = string.Empty;
string date = string.Empty;
string dname = string.Empty;
if (employee.Properties["description"].Value != null)
{
desc = employee.Properties["description"].Value.ToString();
}
if (employee.Properties["whenChanged"].Value != null)
{
date = employee.Properties["whenChanged"].Value.ToString();
}
if (employee.Properties["distinguishedName"].Value != null)
{
dname = employee.Properties["distinguishedName"].Value.ToString();
if (dname.Contains("DC=academic"))
{
dname = "academic";
}
}
}
}
}
Relevant New Code:
using (var results = dirSearcher2.FindAll())
{
foreach (SearchResult searchResult2 in results)
{
html.Append("<tr><td>" + item.ToString() + "</td>");
if (searchResult2.Properties.Contains("description"))
{
desc = searchResult2.Properties["description"][0].ToString();
}
if (searchResult2.Properties.Contains("whenChanged"))
{
date = searchResult2.Properties["whenChanged"][0].ToString();
}
if (searchResult2.Properties.Contains("distinguishedName"))
{
dom = searchResult2.Properties["distinguishedName"][0].ToString();
if (dom.Contains("DC=academic"))
{
dname = "academic";
}
else if (dom.Contains("DC=office"))
{
dname = "office";
}
else
{
dname = "not listed";
}
}
html.Append("<td>" + desc + "</td><td>" + dname + "</td><td>" + date + "</td></tr>");
}
Essentially, I'm getting the same results as it was getting with my first code, IE not getting the correct information on the second Group. IE: i have two groups named AppDev, both are on different domains; however, both show academic in the display. When I look in AD, i see that the distiguished name shows DC=office on one group, though the code above isn't pulling that.
FindOne() only finds one. If you need to see more, you will need to use FindAll(). Just make sure you wrap the result in a using statement, since the documentation says that you can have memory leaks if you don't:
using (var results = dirSearcher2.FindAll()) {
foreach (SearchResult searchResult2 in results) {
//do stuff
}
}
If you only want to find 2 (for example, if you only need to know if more than one exists), then you can set the SizeLimit property of your DirectorySearcher to 2:
dirSearcher2.SizeLimit = 2;
A note about efficiency: When you use .GetDirectoryEntry() and then get the properties from the DirectoryEntry object, DirectoryEntry actually goes back out to AD to get those attributes, even though you already got them during your search. You already used PropertiesToLoad to ask for those attributes, so they are already available in your SearchResult object. Just be aware that all attributes in the Properties list of SearchResult are presented as arrays, so you always need to use [0], even if they are single-valued attributes in AD.
if (searchResult2.Properties.Contains("description")) {
desc = searchResult2.Properties["description"][0];
}
If also need to make sure you are searching the Global Catalog, which will return results from all domains in your forest. You do this by creating the DirectoryEntry that you use for your SearchRoot with GC:// instead of LDAP://. This tell it to use port 3268 (the GC port) rather than the default LDAP port (389). You are creating this object in your CreateDirectoryEntry() method.

Trying to get NetSuite Country list with enumeration value linked to code and name

I am implementing a integration with NetSuite in C#. In the external system I need to populate a list of countries that will match NetSuite's country list.
The NetSuite Web Service provides an enumeration call Country
public enum Country {
_afghanistan,
_alandIslands,
_albania,
_algeria,
...
You can also get a list of country Name and Code (in an albeit not so straight forward way) from the web service. (See: http://suiteweekly.com/2015/07/netsuite-get-all-country-list/)
Which gives you access to values like this:
Afghanistan, AF
Aland Islands, AX
Albania, AL
Algeria, DZ
American Samoa, AS
...
But, as you can see, there is no way to link the two together. (I tried to match by index but that didn't work and sounds scary anyway)
NetSuite's "help" files have a list. But this is static and I really want a dynamic solution that updates as NetSuites updates because we know countries will change--even is not that often.
Screenshot of Country Enumerations from NetSuite help docs
The only solutions I have found online are people who have provided static data that maps the two sets of data. (ex. suiteweekly.com /2015/07/netsuite-complete-country-list-in-netsuite/)
I cannot (don't want to) believe that this is the only solution.
Anyone else have experience with this that has a better solution?
NetSuite, if you are reading, come on guys, give a programmer a break.
The best solution I have come up with is to leverage the apparent relationship between the country name and the enumeration key to forge a link between the two. I am sure others could improve on this solution but what I would really like to see is a solution that isn't a hack like this that relies on an apparent pattern but rather on that is based on an explicit connection. Or better yet NetSuite should just provide the data in one place all together.
For example you can see the apparent relationship here:
_alandIslands -> Aland Islands
With a little code I can try to forge a match.
I first get the Enumeration Keys into an array. And I create a list of objects of type NetSuiteCountry that will hold my results.
var countryEnumKeys = Enum.GetNames(typeof(Country));
var countries = new List<NetSuiteCountry>();
I then loop through the list of country Name and Code I got using the referenced code above (not shown here).
For each country name I then strip all non-word characters from the country name with Regex.Replace, prepend an underscore (_) and then convert the string to lowercase. Finally I try to find a match between the Enumeration Key (converted to lowercase as well) and the matcher string that was created. If a match is found I save all the data together the countries list.
UPDATE: Based on the comments I have added additional code/hacks to try to deal with the anomalies without hard-coding exceptions. Hopefully these updates will catch any future updates to the country list as well, but no promises. As of this writing it was able to handle all the known anomalies. In my case I needed to ignore Deprecated countries so those aren't included.
foreach (RecordRef baseRef in baseRefList)
{
var name = baseRef.name;
//Skip Deprecated countries
if (name.EndsWith("(Deprecated)")) continue;
//Use the name to try to find and enumkey match and only add a country if found.
var enumMatcher = $"_{Regex.Replace(name, #"\W", "").ToLower()}";
//Compares Ignoring Case and Diacritic characters
var enumMatch = CountryEnumKeys.FirstOrDefault(e => string.Compare(e, enumMatcher, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) == 0);
//Then try by Enum starts with Name but only one.
if (enumMatch == null)
{
var matches = CountryEnumKeys.Where(e => e.ToLower().StartsWith(enumMatcher));
if (matches.Count() == 1)
{
Debug.Write($"- Country Match Hack 1 : ");
enumMatch = matches.First();
}
}
//Then try by Name starts with Enum but only one.
if (enumMatch == null)
{
var matches = CountryEnumKeys.Where(e => enumMatcher.StartsWith(e.ToLower()));
if (matches.Count() == 1)
{
Debug.Write($"- Country Match Hack 2 : ");
enumMatch = matches.First();
}
}
//Finally try by first half Enum and Name match but again only one.
if (enumMatch == null)
{
var matches = CountryEnumKeys.Where(e => e.ToLower().StartsWith(enumMatcher.Substring(0, (enumMatcher.Length/2))));
if (matches.Count() == 1)
{
Debug.Write($"- Country Match Hack 3 : ");
enumMatch = matches.First();
}
}
if (enumMatch != null)
{
var enumIndex = Array.IndexOf(CountryEnumKeys, enumMatch);
if (enumIndex >= 0)
{
var country = (Country) enumIndex;
var nsCountry = new NetSuiteCountry
{
Name = baseRef.name,
Code = baseRef.internalId,
EnumKey = country.ToString(),
Country = country
};
Debug.WriteLine($"[{nsCountry.Name}] as [{nsCountry.EnumKey}]");
countries.Add(nsCountry);
}
}
else
{
Debug.WriteLine($"Could not find Country match for: [{name}] as [{enumMatcher}]");
}
}
Here is my NetSuiteCountry class:
public class NetSuiteCountry
{
public string Name { get; set; }
public string Code { get; set; }
public string EnumKey { get; set; }
public Country Country { get; set; }
}
Let me start off with a disclaimer that I'm not a coder, and this is the first day I've tried to look at a C# program.
I need something similar for a Javascript project where I need the complete list of Netsuite company names, codes and their numeric values and when reading the help it seemed like the only way was through webservices.
I downloaded the sample application for webservices from Netsuite and a version of Visual Studio and I was able to edit the sample program provided to create a list of all of the country names and country codes (ex. Canada, CA).
I started out doing something similar to the previous poster to get the list of country names:
string[] countryList = Enum.GetNames(typeof(Country));
foreach (string s in countryList)
{
_out.writeLn(s);
}
But I later got rid of this and started a new technique. I created a class similar to the previous answer:
public class NS_Country
{
public string countryCode { get; set; }
public string countryName { get; set; }
public string countryEnum { get; set; }
public string countryNumericID { get; set; }
}
Here is the new code for getting the list of company names, codes and IDs. I realize that it's not very efficient as I mentioned before I'm not really a coder and this is my first attempt with C#, lots of Google and cutting/pasting ;D.
_out.writeLn(" Attempting to get Country list.");
// Create a list for the NS_Country objects
List<NS_Country> CountryList = new List<NS_Country>();
// Create a new GetSelectValueFieldDescription object to use in a getSelectValue search
GetSelectValueFieldDescription countryDesc = new GetSelectValueFieldDescription();
countryDesc.recordType = RecordType.customer;
countryDesc.recordTypeSpecified = true;
countryDesc.sublist = "addressbooklist";
countryDesc.field = "country";
// Create a GetSelectValueResult object to hold the results of the search
GetSelectValueResult myResult = _service.getSelectValue(countryDesc, 0);
BaseRef[] baseRef = myResult.baseRefList;
foreach (BaseRef nsCountryRef in baseRef)
{
// Didn't know how to do this more efficiently
// Get the type for the BaseRef object, get the property for "internalId",
// then finally get it's value as string and assign it to myCountryCode
string myCountryCode = nsCountryRef.GetType().GetProperty("internalId").GetValue(nsCountryRef).ToString();
// Create a new NS_Country object
NS_Country countryToAdd = new NS_Country
{
countryCode = myCountryCode,
countryName = nsCountryRef.name,
// Call to a function to get the enum value based on the name
countryEnum = getCountryEnum(nsCountryRef.name)
};
try
{
// If the country enum was verified in the Countries enum
if (!String.IsNullOrEmpty(countryToAdd.countryEnum))
{
int countryEnumIndex = (int)Enum.Parse(typeof(Country), countryToAdd.countryEnum);
Debug.WriteLine("Enum: " + countryToAdd.countryEnum + ", Enum Index: " + countryEnumIndex);
_out.writeLn("ID: " + countryToAdd.countryCode + ", Name: " + countryToAdd.countryName + ", Enum: " + countryToAdd.countryEnum);
}
}
// There was a problem locating the country enum that was not handled
catch (Exception ex)
{
Debug.WriteLine("Enum: " + countryToAdd.countryEnum + ", Enum Index Not Found");
_out.writeLn("ID: " + countryToAdd.countryCode + ", Name: " + countryToAdd.countryName + ", Enum: Not Found");
}
// Add the countryToAdd object to the CountryList
CountryList.Add(countryToAdd);
}
// Create a JSON - I need this for my javascript
var javaScriptSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
string jsonString = javaScriptSerializer.Serialize(CountryList);
Debug.WriteLine(jsonString);
In order to get the enum values, I created a function called getCountryEnum:
static string getCountryEnum(string countryName)
{
// Create a dictionary for looking up the exceptions that can't be converted
// Don't know what Netsuite was thinking with these ones ;D
Dictionary<string, string> dictExceptions = new Dictionary<string, string>()
{
{"Congo, Democratic Republic of", "_congoDemocraticPeoplesRepublic"},
{"Myanmar (Burma)", "_myanmar"},
{"Wallis and Futuna", "_wallisAndFutunaIslands"}
};
// Replace with "'s" in the Country names with "s"
string countryName2 = Regex.Replace(countryName, #"\'s", "s");
// Call a function that replaces accented characters with non-accented equivalent
countryName2 = RemoveDiacritics(countryName2);
countryName2 = Regex.Replace(countryName2, #"\W", " ");
string[] separators = {" ","'"}; // "'" required to deal with country names like "Cote d'Ivoire"
string[] words = countryName2.Split(separators, StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < words.Length; i++)
{
string word = words[i];
if (i == 0)
{
words[i] = char.ToLower(word[0]) + word.Substring(1);
}
else
{
words[i] = char.ToUpper(word[0]) + word.Substring(1);
}
}
string countryEnum2 = "_" + String.Join("", words);
// return an empty string if the country name contains Deprecated
bool b = countryName.Contains("Deprecated");
if (b)
{
return String.Empty;
}
else
{
// test to see if the country name was one of the exceptions
string test;
bool isExceptionCountry = dictExceptions.TryGetValue(countryName, out test);
if (isExceptionCountry == true)
{
return dictExceptions[countryName];
}
else
{
return countryEnum2;
}
}
}
In the above I used a function, RemoveDiacritics I found here. I will repost the referenced function below:
static string RemoveDiacritics(string text)
{
string formD = text.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
foreach (char ch in formD)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(ch);
}
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
Here are the tricky cases to test any solution you develop with:
// Test tricky names
Debug.WriteLine(getCountryEnum("Curaçao"));
Debug.WriteLine(getCountryEnum("Saint Barthélemy"));
Debug.WriteLine(getCountryEnum("Croatia/Hrvatska"));
Debug.WriteLine(getCountryEnum("Korea, Democratic People's Republic"));
Debug.WriteLine(getCountryEnum("US Minor Outlying Islands"));
Debug.WriteLine(getCountryEnum("Cote d'Ivoire"));
Debug.WriteLine(getCountryEnum("Heard and McDonald Islands"));
// Enums that fail
Debug.WriteLine(getCountryEnum("Congo, Democratic Republic of")); // _congoDemocraticPeoplesRepublic added to exceptions
Debug.WriteLine(getCountryEnum("Myanmar (Burma)")); // _myanmar added to exceptions
Debug.WriteLine(getCountryEnum("Netherlands Antilles (Deprecated)")); // Skip Deprecated
Debug.WriteLine(getCountryEnum("Serbia and Montenegro (Deprecated)")); // Skip Deprecated
Debug.WriteLine(getCountryEnum("Wallis and Futuna")); // _wallisAndFutunaIslands added to exceptions
For my purposes I wanted a JSON object that had all the values for Coutries (Name, Code, Enum, Value). I'll include it here in case anyone is searching for it. The numeric values are useful when you have a 3rd party HTML form that has to forward the information to a Netsuite online form.
Here is a link to the JSON object on Pastebin.
My appologies for the lack of programming knowledge (only really do a bit of javascript), hopefully this additional information will be useful for someone.

How to take the last line of the same Name from text file?

i'm trying to load some data into gridview, sorry if my phrasing is bad or weird, but what i mean exactly is, lets say my text file has this data
Name1,123
Name1,133
Name1,143
Name2,533
Name2,233
Name2,973
Name2,313
Name3,533
Name3,233
Name3,973
Name3,213
...
and i only want to take the last line of Name1, Name2, Name3 and inserting to gridview
Name1,143
Name2,313
Name3,213
subsequently, i'll be trying to update the gridview cells, if further down in the text file there is the same Name again. but yeah. i'm trying to even get this part first.
so i have been trying to use foreach to try to readline and like add them all in and then removing the ones that have the same Name. but it's not removing any of it.
my codes currently is
public class LiveData
{
public string Name { get; set; }
public double Price { get; set; }
public static List<LiveData> LoadDataFromFile(string path)
{
var data = new List<LiveData>();
foreach (var line in File.ReadAllLines(path))
{
var dataLine = line.Split(',');
data.Add(new LiveData
{
Name = dataLine[0],
Price = Convert.ToDouble(dataLine[1])
});
}
return data;
}
}
and
private void btnLoadLDgridView_Click(object sender, EventArgs e)
{
StreamReader file = new StreamReader(tbTSSelectFile.Text);
List<LiveData> list = LiveData.LoadDataFromFile(tbTSSelectFile.Text);
List<LiveData> lst2 = LiveData.LoadDataFromFile(tbTSSelectFile.Text);
while (file.ReadLine() != null)
{
string name = lst2[0].Name;
foreach (LiveData item in list)
{
if (item.Name == name)
{
lst2.Add(item);
}
}
foreach (LiveData item in list)
{
if (item.Name == name)
{
lst2.Remove(item);
}
}
gvLiveData.DataSource = lst2;
}
i'm thinking maybe .Last can be used but i am not sure how to implement it.
the text file data is supposedly Live Stocks data, hence the need to update later on.
thanks in advance :)
After you've parsed your file into the data variable, you could try using Linq:
var lastItems = data.GroupBy(d => d.Name).Select(g => g.Last());
This utilizes the GroupBy and Last operators.

How do you get the index of a character in a string when it's less than your starting index?

As per my question, I want to get the index of the first comma prior to my current starting index. To give an example of the data I have a string like this:
Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM
The string contains elements setup as [Persons Name];[Role], so the name is separated from the role by a ; (semi-colon) and each element is separated from each other with a , (comma).
The elements in the string can be in any order, so the reason for my question is that I want to get the person's name out for the role OW. My initial thoughts were to get the index of ;OW, and somehow work back from there. I can obviously loop backwards through the string from my starting index checking to see if the character is a comma but that seems inefficient, so is there a better way to achieve this?
EDIT
To clarify, I only want to get the name associated with the role OW. This role SHOULD only occur in the string once. If it doesn't then I'm happy to only get the first occurrence, which I think IndexOf(";OW,") will do. I don't need the other roles or names, just the name associated with OW.
Also, roles will only ever be 2 characters long. As Matt Burland pointed out, if it's at the end of the string it won't have a trailing comma. However, I can amend my indexof to simply search for ";OW" as roles are only 2 characters long.
Use String.Split(',') to split your string on the comma into an Array. Then make a custom object:
public RoledPerson{
public string Person;
public string Role;
public RoledPerson(string input){
string[] splitInput = input.Split(';');
Person = splitInput[0];
Role = splitInput[1];
}
}
Then you can convert your string into an Enumerable as follows:
var RoledPersons inputstring.Split(',').Select(string => new RoledPerson(string));
Then you can just find whatever RoledPerson has OW as his role:
var RoledPersonsWithRole = RoledPersons.Where(roledperson => roledperson.Role == "OW");
As Matt Burland said, you can also do this with a Dictionary<string, string>. I'll leave how to work this out to you. However, this doesn't support multiple keys with the same name, so this won't work if you have the same role multiple times.
Disclaimer: there might be errors in here.
how about a regular expression. this should work:
string role = "OW";
string str = "Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM";
string pattern = "([^,]*);" + role;
var match = Regex.Match(str, pattern);
if (match.Success)
{
Console.WriteLine(match.Groups[1].Value);
}
Use the string.LastIndexOf overload that includes the starting position:
string s = "Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM";
int startRole = s.IndexOf(";OW");
int startName = s.LastIndexOf(',',startRole) + 1; // start at the semicolon before the role
string name s.Substring(startName,(startRole-startName));
Note that there are edge cases that need to be considered:
Are all roles two characters (e.g. could there be a OWX role)?
If the OW role is the first in the list there will be no comma before it
Are there multiple OW roles? If so you could use a while loop and just start the search at the end of the previous role string.
This is a basic way to get all OW not just the first or the last one. Feed in OW to the second function.
class stringSections
{
private List<string> role = new List<string>();
private List<string> name = new List<string>();
public void Input(string input)
{
string temp = "";
for(int i =0;i<input.Length;i++)
{
if(input[i]==';')
{
name.Add(temp);
temp = "";
} else if(input[i]==',')
{
role.Add(temp);
temp = "";
} else
{
temp += input[i];
}
}
}
public List<string> GetAll(string prole)
{
List<string> reterners = new List<string>();
for(int i = 0; i < role.Count;i++)
{
if (role[i] == prole)
{
reterners.Add(name[i]);
}
}
return reterners;
}
}
To handle the edge cases raised by #D Stanley:
var s = "Bob Williams;OW,Bob Green;PD,Frank Williams;OW,Andy Richards;BD,James Clack;PM,Dave Williams;OW";
var r = new Regex("(;OW,|^OW,|;OW$)");
if (r.IsMatch(s))
{
foreach (Match m in r.Matches(s))
{
var rIdx = m.Index;
var pIdx = s.LastIndexOf(",",rIdx);
var person = s.Substring(pIdx + 1, rIdx - pIdx - 1);
Console.WriteLine(person);
}
}
else
{
Console.WriteLine("Role not found");
}
To ensure my 'solution' worked I coded a quick apsx web form with the following :
<asp:Content ID="Content3" ContentPlaceHolderID="PageContent" runat="server">
<asp:HiddenField ID="TestString" runat="server" Value="Bob Green;PD,Andy Richards;BD,Frank Williams;OW,James Clack;PM" />
<asp:Label ID="Label1" runat="server" Text="Find people in role"></asp:Label>
<asp:TextBox ID="RoleToFind" runat="server"></asp:TextBox><br /><br />
<asp:TextBox ID="Result" runat="server" Rows="10" TextMode="MultiLine" Width="294px"></asp:TextBox>
<asp:Button ID="SearchButton" runat="server" Text="Search" OnClick="SearchButton_Click" />
I then created a class as per Nate's answer
[Serializable]
public class RolePerson
{
public string Person { get; set; }
public string Role { get; set; }
}
And finally in the code behind the aspx page I have added the following:
public partial class teststack : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void SearchButton_Click(object sender, EventArgs e)
{
List<RolePerson> lrp = new List<RolePerson>();
// Get the string from the hidden field
string strData = this.TestString.Value;
// split the string into an array each value in the array haveing name;role
string[] strRecords = strData.Split(new Char[] {','});
// process the array and add to the list
foreach (string s in strRecords)
{
string[] strRecord = s.Split(new Char[] { ';' });
lrp.Add(new RolePerson{
Person = strRecord[0],
Role = strRecord[1]
});
}
// Find the person for the specified role
FindPerson(lrp, this.RoleToFind.Text);
}
//Find the people for the specified role and add to the results textbox
private void FindPerson(List<RolePerson> lrp, string strRole)
{
this.Result.Text = null;
string strResults = string.Empty;
foreach (RolePerson rp in lrp)
{
if (rp.Role == strRole)
strResults = strResults + rp.Person + "\r\n";
}
this.Result.Text = strResults;
}
}
And the result :
Using the above you would be able to find multiple people in the same role and allow the role to be specified by the user.
The FindPerson could be developed further to use Linq query which would be more efficient on a much larger string.

Categories