How to extract from a list based on user input - c#

The code contains two methods.
The Main which prompts the user for input and prints a sublist based on said user input.
The Extract method passes query from user input and adds all indices to dbQueryList to be extracted from dbListing and printed as query.
How does one to add to a List based on user input?
The primary issue is the if statement which contains the condition of
i.Substring(0, query.Length) = query. This is meant to test the condition 'if part of the query exists in any index in dbListing, add elements to dbQueryList '.
I originally wrote this in Python and it worked perfectly fine. I'm learning C# and not sure how to change that if condition. I considered changing the code and use LINQ in the foreach loop but not entirely clear on how to implement that.
Looking forward to community feedback! :)
//**************************************************
// File Name: autocomplete.cs
// Version: 1.0
// Description: Create a method that functions like an autocomplete
// API and truncates search to 5 results.
// Last Modified: 12/19/2018
//**************************************************
using System;
using System.Collections.Generic;
namespace autocomplete
{
class Program
{
private static string[] database;
private static string input;
private static string query;
static void Main(string[] args)
{
// user input to pass as query
Console.Write("Enter your query: ");
string query = Console.ReadLine();
// dynamic list comprised of 'database' array
List<string> dbListing = new List<string>();
string[] database = new string[] { "abracadara", "al", "alice", "alicia", "allen", "alter","altercation", "bob", "element", "ello", "eve", "evening", "event", "eventually", "mallory" };
dbListing.AddRange(database);
// write results based on user query
Console.WriteLine("Your results: " + Extract(Program.query));
// keep console window open after displaying results
Console.ReadLine();
}
// extract method passing query, return dbQueryList as query
public static List<string> Extract(string query)
{
// empty list is initiated
List<string> dbQueryList = new List<string>();
// foreach assesses all strings in database in main
// then, appends all indices of list equal to given query
foreach (string i in database)
{
// compares query (from index 0 to length of) to all strings in database
if (i.Substring(0, query.Length) = query)
{
// add to list above based on query
dbQueryList.Add(i);
}
// if statement truncates dbQueryList to 5 results
if (dbQueryList.Capacity >= 5)
break;
}
return dbQueryList;
}
}
UPDATE: 1/3/2019 18:30
I made the following changes to the Extract(query) and it worked!
foreach (string i in database)
{
// compares query (from index 0 to length of) to all strings in database
if (i.StartsWith(query))
{
// add to list above based on query
dbQueryList.Add(i);
Console.WriteLine(i);
}
// if statement truncates dbQueryList to 5 results
if (dbQueryList.Capacity >= 5)
break;
}
return dbQueryList;
Very excited that I got this to work! Please let me know if there are any further feedback about how to improve and clean this code if necessary! Cheers, everyone!

The problem is you are using = instead of == in the if statement.
In C# = operator is for assignment so what you are doing is trying to assign query to the expression on the left side, which is not possible. Instead use == operator which is for comparison.
Also, there is a more suitable method - use i.StartsWith(query) to check if the string starts with the given query. The current solution would work as long as i is not shorter than query.Length, in which case it would throw an exception.
if (i.StartsWith(query))
{
...

Related

How to do this kind of search in ASP.net MVC?

I have an ASP.NET MVC web application.
The SQL table has one column ProdNum and it contains data such as 4892-34-456-2311.
The user needs a form to search the database that includes this field.
The problem is that the user wants to have 4 separate fields in the UI razor view whereas each field should match with the 4 parts of data above between -.
For example ProdNum1, ProdNum2, ProdNum3 and ProdNum4 field should match with 4892, 34, 456, 2311.
Since the entire search form contains many fields including these 4 fields, the search logic is based on a predicate which is inherited from the PredicateBuilder class.
Something like this:
...other field to be filtered
if (!string.IsNullOrEmpty(ProdNum1) {
predicate = predicate.And(
t => t.ProdNum.toString().Split('-')[0].Contains(ProdNum1).ToList();
...other fields to be filtered
But the above code has run-time error:
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities`
Does anybody know how to resolve this issue?
Thanks a lot for all responses, finally, I found an easy way to resolve it.
instead of rebuilding models and change the database tables, I just add extra space in the search strings to match the search criteria. since the data format always is: 4892-34-456-2311, so I use Startwith(PODNum1) to search first field, and use Contains("-" + PODNum2 + "-") to search second and third strings (replace PODNum1 to PODNum3), and use EndWith("-" + PODNum4) to search 4th string. This way, I don't need to change anything else, it is simple.
Again, thanks a lot for all responses, much appreciated.
If i understand this correct,you have one column which u want to act like 4 different column ? This isn't worth it...For that,you need to Split each rows column data,create a class to handle the splitted data and finally use a `List .Thats a useless workaround.I rather suggest u to use 4 columns instead.
But if you still want to go with your existing applied method,you first need to Split as i mentioned earlier.For that,here's an example :
public void test()
{
SqlDataReader datareader = new SqlDataReader;
while (datareader.read)
{
string part1 = datareader(1).toString.Split("-")(0);///the 1st part of your column data
string part2 = datareader(1).toString.Split("-")(1);///the 2nd part of your column data
}
}
Now,as mentioned in the comments,you can rather a class to handle all the data.For example,let's call it mydata
public class mydata {
public string part1;
public string part2;
public string part3;
public string part4;
}
Now,within the While loop of the SqlDatareader,declare a new instance of this class and pass the values to it.An example :
public void test()
{
SqlDataReader datareader = new SqlDataReader;
while (datareader.read)
{
Mydata alldata = new Mydata;
alldata.Part1 = datareader(1).toString.Split("-")(0);
alldata.Part2 = datareader(1).toString.Split("-")(1);
}
}
Create a list of the class in class-level
public class MyForm
{
List<MyData> storedData = new List<MyData>;
}
Within the while loop of the SqlDatareader,add this at the end :
storedData.Add(allData);
So finally, u have a list of all the splitted data..So write your filtering logic easily :)
As already mentioned in a comment, the error means that accessing data via index (see [0]) is not supported when translating your expression to SQL. Split('-') is also not supported hence you have to resort to the supported functions Substring() and IndexOf(startIndex).
You could do something like the following to first transform the string into 4 number strings ...
.Select(t => new {
t.ProdNum,
FirstNumber = t.ProdNum.Substring(0, t.ProdNum.IndexOf("-")),
Remainder = t.ProdNum.Substring(t.ProdNum.IndexOf("-") + 1)
})
.Select(t => new {
t.ProdNum,
t.FirstNumber,
SecondNumber = t.Remainder.Substring(0, t.Remainder.IndexOf("-")),
Remainder = t.Remainder.Substring(t.Remainder.IndexOf("-") + 1)
})
.Select(t => new {
t.ProdNum,
t.FirstNumber,
t.SecondNumber,
ThirdNumber = t.Remainder.Substring(0, t.Remainder.IndexOf("-")),
FourthNumber = t.Remainder.Substring(t.Remainder.IndexOf("-") + 1)
})
... and then you could simply write something like
if (!string.IsNullOrEmpty(ProdNum3) {
predicate = predicate.And(
t => t.ThirdNumber.Contains(ProdNum3)

Linq OrderBy not sorting correctly 100% of the time

I'm using the Linq OrderBy() function to sort a generic list of Sitecore items by display name, then build a string of pipe-delimited guids, which is then inserted into a Sitecore field. The display name is a model number of a product, generally around 10 digits. At first it seemed like this worked 100% of the time, but the client found a problem with it...
This is one example that we have found so far. The code somehow thinks IC-30R-LH comes after IC-30RID-LH, but the opposite should be true.
I put this into an online alphabetizer like this one and it was able to get it right...
I did try adding StringComparer.InvariantCultureIgnoreCase as a second parameter to the OrderBy() but it did not help.
Here's the code... Let me know if you have any ideas. Note that I am not running this OrderBy() call inside of a loop, at any scope.
private string GetAlphabetizedGuidString(Item i, Field f)
{
List<Item> items = new List<Item>();
StringBuilder scGuidBuilder = new StringBuilder();
if (i != null && f != null)
{
foreach (ID guid in ((MultilistField)f).TargetIDs)
{
Item target = Sitecore.Data.Database.GetDatabase("master").Items.GetItem(guid);
if (target != null && !string.IsNullOrEmpty(target.DisplayName)) items.Add(target);
}
// Sort it by item name.
items = items.OrderBy(o => o.DisplayName, StringComparer.InvariantCultureIgnoreCase).ToList();
// Build a string of pipe-delimited guids.
foreach (Item item in items)
{
scGuidBuilder.Append(item.ID);
scGuidBuilder.Append("|");
}
// Return string which is a list of guids.
return scGuidBuilder.ToString().TrimEnd('|');
}
return string.Empty;
}
I was able to reproduce your problem with the following code:
var strings = new string[] { "IC-30RID-LH", "IC-30RID-RH", "IC-30R-LH", "IC-30R-RH"};
var sorted = strings.OrderBy(s => s);
I was also able to get the desired sort order by adding a comparer to the sort.
var sorted = strings.OrderBy(s => s, StringComparer.OrdinalIgnoreCase);
That forces a character-by-character (technically byte-by-byte) comparison of the two strings, which puts the '-' (45) before the 'I' (73).

Querying parse in unity project returns all blank data

I am implementing the Parse Unity SDK in order to have a high score system. I run a query on my data to get the top ten players and their scores. (It should be sorted by score). For some reason when my code is run, I get a blank string for the name and a 0 for the score even though my data has real values in it.
Here is the query:
int[] scores = new int[10];
string[] names = new string[10];
int i = 0;
var query = ParseObject.GetQuery ("HighScores").OrderByDescending ("score").Limit (10);
query.FindAsync().ContinueWith (t =>
{
IEnumerable<ParseObject> results = t.Result;
foreach (var obj in results)
{
scores[i] = obj.Get<int>("score");
names[i] = obj.Get<string>("playerName");
i++;
}
});
The class name is "HighScores" and I am trying to access the score ("score") and player name ("playerName") of each saved entry.
EDIT:
I found that there are zero results returned so it must be something with the query. I don't see what could be wrong with it.
8/17/15
I still have not found out what is going on with my query. Any ideas?
It turns out that I was getting the data from the query. The query was fine all along. The actual issue was that I was trying to output my newly found scores to a string that was being called before the query was finished getting the data since the query is an asynchronous call. Instead of doing it this way, I let the query run fully and once the query is finished, I set a static bool called finishedRunningQuery to true. Now in the update() method I have it check: if (finishedRunningQuery) then update the high score text. This fixes the issue.

Reading a CSV file and extracting specific data [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
So I have a .CSV file which has possibly several millions, maybe even billions lines of data. The data is in the format below:
1,,5,6,7,82,4,6
1,4,4,5,6,33,4,
2,6,3,,6,32,6,7
,,,2,5,45,,6
,4,5,6,,33,5,6
What I am trying to achieve is this: Lets assume each line of data is an "event". Lets call it that. Now lets say an user says, show me all events where the 6th value is 33. You can see above that the 6th data element is a 2 digit number and the user can say show me all events where the 6th data element is 33 and the output would be:
1,4,4,5,6,33,4,
,4,5,6,,33,5,6
Also, as you can see. The data can have blanks or holes where data is missing. I don't need help reading a .CSV file or anything. I just cant wrap my mind around how I would access the 6th data element. Also, I would prefer if this output is represented in a collection of some sort maybe. I'm new to C# so I don't have much knowledge about the inbuilt classes. Any help will be appreciated!
I suggest instead of using term "event" to call this data structure more customarily as "rows and columns" and use C# Split() function to create 2d-array (string[,] or int[,]), where each element is conveniently accessible by its row/column index, and to apply whatever business logic to those elements.
Possible implementation of the CSV file reader (by line, with each line stored in the List<string> listRows) is shown below (re: Reading CSV file and storing values into an array)
using System.IO;
static void Main(string[] args)
{
var reader = new StreamReader(File.OpenRead(#"C:\YouFile.csv"));
List<string> listRows= new List<string>();
while (!reader.EndOfStream)
{
listRows.Add(reader.ReadLine());
}
}
Then apply Split(',') function to each row (stored in listRows) to compose a 2d-array string[,] and use int.TryParse() method to convert it to type int (optional, upon necessity).
Alternatively, this could be implemented by using LINQ Library, which is not recommended because of unnecessary extension of the technology surface area, plus possible performance degradation (LINQ solution expected to be slower than suggested direct processing).
Hope this may help.
Using Linq it is pretty easy to achieve. I'm posting as sample from LinqPad and providing output. All you need to do is to replace 33 with a parameter:
void Main()
{
string csvFile = #"C:\Temp\TestData.csv";
string[] lines = File.ReadAllLines(csvFile);
var values = lines.Select(s => new { myRow = s.Split(',')});
//and here is your collection representing results
List<string[]> results = new List<string[]>();
foreach (var value in values)
{
if(value.Values.Contains("33")){
results.Add(value.myRow);
}
}
results.Dump();
}
Output:
or if you want you can have it all in one shot by doing this
string csvFile = #"C:\Temp\TestData.csv";
string[] lines = File.ReadAllLines(csvFile);
var values = lines.Select(s =>
new {Position =Array.FindIndex(s.Split(','),a=>a.Contains("33"))+1
,myRow = s.Split(',')
});
so the final product will have both - the position of your search (33) and the complete string[] of items.
Create a class EventEntity. In this class create a List<int> with a constructor that initializes the list. Here is a class example:
public class EventEntity
{
public EventEntity()
{
EventList = new List<int>();
}
public List<int> EventList { get; set; }
}
From there loop through each row of data. Example:
public class EventEntityRepo
{
public EventEntity GetEventEntityByCsvDataRow(String[] csvRow)
{
EventEntity events = new EventEntity();
foreach (String csvCell in csvRow)
{
int eventId = -1;
if(csvCell != null && csvCell != String.Empty)
{
try
{
eventId = Convert.ToInt32(csvCell.Trim());
}
catch (Exception ex)
{
//failed to parse int
}
}
events.EventList.Add(eventId); //if an empty item, insert -1
}
return events;
}
}
Then you can reference the items whenever you want.
eventEntityList = GetEventEntityByCsvDataRow(csvDataRow);
eventEntitySixthElement = eventEntityList[5];
So your questions is how to access the 6th data element. It's not too hard if you have right data structure representing your csv.
Basically this csv document in abstract term can be described as IEnumerable<IEnumerable<String>>, or, maybe, IEnumerable<IEnumerable<int?>>. Having implemented csv parsing logic, you will access the 6th elements by executin:
var csvRepresenation = ParseCsv(#"D:/file.csv");
var element = csvRepresentation.ElementAt(6);
if (element == "6")
{
// do smth
}
With this aproach you will also be able to execute Linq statements on it.
Now the question is how you will implement the ParseCsv():
public IEnumerable<IEnumerable<String>> ParseCsv(string path)
{
return File.ReadAllLines(path).Select(row => row.Split(','));
}

ASP.NET MVC 3 - Search with multiple terms

I have a method where by I'm able to search in a database for a specific customer(s). At the moment it only takes 1 term, but i'd like to be able to search with multiple terms (for example the customer's account number and their name). Below is my method:
public List<AXCustomer> allCustomers(string id)
{
string[] searchstring = id.Split(' ');
List<AXCustomer> customer = new List<AXCustomer>();
// if 3 terms are entered
if (searchstring.Length > 2)
{
}
// if 2 terms are entered
else if (searchstring.Length > 1)
{
}
// revert back to default search
else
{
customer = context.AXCustomers.Where(x => x.ACCOUNTNUM.Contains(id) ||
x.NAME.Contains(id) || x.ZIPCODE.Contains(id)).ToList();
}
return customer;
}
As you can see, i've decided to split each term entered (I assume each term will be seperated by a space) but I'm not sure how my LINQ query should be for terms longer than one. Any help would be appreciated
Since you don't know what will be entered or how long it will be, I would suggest doing the following:
public List<AXCustomer> allCustomers(string id)
{
string[] searchstring = id.Split(' ');
List<List<AXCustomer>> customerlists = new List<List<AXCustomer>>();
foreach (string word in searchstring)
{
customerlists.Add(context.AXCustomers.Where(x => x.ACCOUNTNUM.Contains(word) || x.NAME.Contains(word) || x.ZIPCODE.Contains(word)).ToList());
}
//Then you just need to see if you want ANY matches or COMPLETE matches.
//Throw your results together in a List<AXCustomer> and return it.
return mycombinedlist;
}
Any matches = throw all lists together, then take the distinct ones.
Complete matches = you'll have to check for items which occur in all customerlists.
It will work fine. I am using a similar type of query in my project and it seems to work great. Following is the code snippet
PagedList.IPagedList<Product> PagedProducts = dbStore.Products.Where(p => p.Name.Contains(query) || p.MetaKeywords.Contains(query)).ToList().ToPagedList(pageIndex, PageSize);
BTW, its running on a live server too.
You can dynamically attach as many conditions as you wish, in the following manner:
customer = context.AXCustomers.Where(x => x.ACCOUNTNUM.Contains(id));
customer = customer.Where(Condition 2);
customer = customer.Where(Condition 3);
And so on. You have full control over the criteria : just make sure that it resolves to a sequel query.

Categories