How to remove zero elements from List<string>? - c#

Read couple of examples, but seems i`m doing something wrong.
Need to remove all "0" strings from a HUGE List without using hashtables.
Tryed using lambda samples from Stack Owerflow and MSDN examples, but looks i`m messing something up.
DataTable book = SQL.SqlGetTable(BookList.SelectedItem.ToString());
List<string> pagesExist = new List<string>();
for (int i = 0; i < book.Rows.Count; i++)
{
pagesExist.Add(book.Rows[i][0].ToString());
}
var found = pagesExist.Find(x => x == "0");
if (found != null)
pagesExist.Remove(found);
I have a pagesExist list of 4000 string elements.
Supposed that
var found = pagesExist.Find(x => x == "0");
will accumulate all zeroes in list and will remove them next string. But somehow found results in 0 elements

No need to create the pagesExist list. Just filter out all non zero rows using a simple linq query over the DataTable. This way your entire code is reduced to only:
DataTable book = SQL.SqlGetTable(BookList.SelectedItem.ToString());
var result = book.AsEnumerable().Where(r => r.Field<int>("FieldName") != 0);
I am assuming that the column contains integers only. If not then keep the use Field<string> and filter for != "0".
As a side note I would recommend looking into SqlGetTable. If it returns a DataTable it already brings all this data into memory from database, something that can be avoided with using linq directly over the DBMS using tools like linq-2-sql or linq-2-entities

You can use RemoveAll :
pagesExist.RemoveAll(p => p == "0");

Related

Fastest way to match members of two lists

I have two lists which are orderHeaders and orderLines. These are two related tables in the database however when I pull them I have to pull them separately as two different lists and then map them out to each other later. I have a solution right now but the performance is a little bit disappointing given that I have around 400k headers and 1million+ lines.
Here's my code below. Is this the standard way to iterate over and find members inside two lists or is there a more optimized approach in C#?
var OutboundOrderHeaders =
DbContext.Context.Database.SqlQuery<OutboundOrderDTO>(queryString, parameter);
var OutboundOrderHeadersList = OutboundOrderHeaders.ToList();
var OutboundOrderLine =
DbContext.Context.Database.SqlQuery<OutboundOrderLineDTO>(queryStringLine, parameter2);
var OutboundOrderLineList = OutboundOrderLine.ToList();
for(var i = 0; i < OutboundOrderHeadersList.Count(); i++)
{
var LineToAdd = OutboundOrderLineList
.Where(x => x.OutboundNumber == OutboundOrderHeadersList[i].OutboundNumber)
.ToList() ;
OutboundOrderHeadersList[i].OrderLine = LineToAdd;
}
return OutboundOrderHeadersList;
As noted in comments, I'd really try hard to do this in the database rather than in memory. But to do it in memory, ToLookup is probably the right way to go:
// Note: here I've renamed used outboundOrderLines where you've got OutboundOrderLineList,
// and orderHeaders where you've got OutboundOrderHeadersList, as simpler
// and more conventional variable names.
var linesByOutboundNumber = outboundOrderLines.ToLookup(line => line.OutboundNumber);
foreach (var orderHeader in orderHeaders)
{
orderHeader.OrderLine = linesByOutboundNumer[orderHeader.OutboundNumber].ToList();
}
This builds a map going from outbound number to "all the lines with that outbound number" by going through outboundOrderLines once, rather than iterating over it for every order header.

Seeking for help for SQL server query to search multiple columns of a table using a combination of keywords

I am having a nightmare in building an efficient search query. I am using LINQ. The requirement is as follows:
In the application there is one text fox field which is used as a Quick Search.
The table I will be searching holds three fields Make, Model and Extension.
A typical keyword user can enter is like Honda Accord XL
Based on the keywords database should return me the matching rows and here the problem starts. There is no restriction on the order which keywords will be entered to prepare the phrase, i.e. one can enter Accord XL Honda or it could be like XL Accord or could just be Honda. In the example Honda is Make, Accord is the Model and XL is the extension.
Ideally the search result should only pull up perfect matches like if Honda Accord is entered it will not bring up other models from Honda. But the major problem is I don't know what they will enter and I have to look into three different columns of the table using Contains operator.
Here is what I tried:
I spit the phrase into words and place them in an array
var arr = keyWord.Split(new [] {' '}). Next step I build the query inside a loop of those array elements:
foreach (var k in arr)
{
var item = new Vehicle();
var arrayItem = k;
var query = DataContext.Vehicles.Where(v =>v.RealName.Contains(arrayItem)
|| v.Model.Contains(arrayItem)
|| v.Style.Contains(arrayItem)).ToList();
foreach (var v in query)
{
if(!result.Contains(v))
result.Add(v);
}
}
return result;
Now when the loop is executing and matching records for Make it already fills the list with say 250 items. But how can I remove unwanted items like when a record has CT as Model or TYL as Extension? If I knew the order of the words in which the keyword was created then I will have the option to remove unmatched Make, Model or Extension from the list by using one line of code for each and return the final result. But in this case if I have to do it I again have to use loop and remove unmatched items, even that will not probably give me the correct data. And definitely this is not the efficient way to do this.
--- Try to use Below way your code first retrieve respective make to database ------then filter gettting previous result.
List<Vehicle> lsvehicle= new List<Vehicle()>;
foreach (var k in arr)
{
var arrayItem = k;
lsvehicle = DataContext.Vehicles.Where(v =>v.RealName.Contains(arrayItem) ).ToList();
}
foreach (var k in arr)
{
lsvehicle = lsvehicle.Where(v =>v.Model.Contains(arrayItem) || v.Style.Contains(arrayItem)).tolist();
}
return lsvehicle ;
This can be achieved by concatenating the Make + Model + Extension string and then comparing that the whole array is contained by this string
var query = DataContext.Vehicles;
foreach (var k in arr)
{
var item = new Vehicle();
var arrayItem = k;
query = query.Where(v => (v.RealName + v.Model + v.Style).Contains(arrayItem)).ToList();
}
return query;
NOTE: Logical answer, may require syntax error correction if any
I would suggest following approach, assuming you have the option to create view in database
and you are searching through three columns
1)Create a view With all combination
2)Use linq to get the record from the view
sql
create view [Vw_VehicleSearch]
AS
Select
M+V+E [MVE],
M+E+V [MEV],
V+M+E [VME],
v+E+M [VEM],
E+M+V [EMV],
E+V+M [EVM]
from
vehicletable
c#
public List<string> Search(string quickSearchText)
{
using(var ctx=new model()))
{
var result=ctx
.Vw_VehicleSearch
.Where(v=>v.MVE.Contains(quickSearchText)
|| v=>v.MEV.Contains(quickSearchText)
.|| v=>v.VME.Contains(quickSearchText)
.|| v=>v.VEM.Contains(quickSearchText)
.|| v=>v.EMV.Contains(quickSearchText)
.|| v=>v.EVM.Contains(quickSearchText)
.ToList();
return result.Select(r=>r.MVE).ToList();
}
}
What you want is: all vehicles of which either RealName, or Model, or Style contains all keywords. This can be achieved by:
var query = DataContext.Vehicles.Where(v =>
arr.All(s => v.RealName.Contains(s))
|| arr.All(s => v.Model.Contains(s))
|| arr.All(s => v.Style.Contains(s)))
.ToList();
Entity Framework is able to translate this query into SQL because it has a trick to convert the array arr into a table (of sorts) that can be used in the SQL statement (you should take a look at the generated SQL).
This is not the most efficient way to run a query. It will become considerably slower when the number of keywords becomes "large". I don't think that will be an issue here though.

EF - A proper way to search several items in database

I have about 100 items (allRights) in the database and about 10 id-s to be searched (inputRightsIds). Which one is better - first to get all rights and then search the items (Variant 1) or to make 10 checking requests requests to the database
Here is some example code:
DbContext db = new DbContext();
int[] inputRightsIds = new int[10]{...};
Variant 1
var allRights = db.Rights.ToLIst();
foreach( var right in allRights)
{
for(int i>0; i<inputRightsIds.Lenght; i++)
{
if(inputRightsIds[i] == right.Id)
{
// Do something
}
}
}
Variant 2
for(int i>0; i<inputRightsIds.Lenght; i++)
{
if(db.Rights.Any(r => r.Id == inputRightsIds[i]);)
{
// Do something
}
}
Thanks in advance!
As other's have already stated you should do the following.
var matchingIds = from r in db.Rights
where inputRightIds.Contains(r.Id)
select r.Id;
foreach(var id in matchingIds)
{
// Do something
}
But this is different from both of your approaches. In your first approach you are making one SQL call to the DB that is returning more results than you are interested in. The second is making multiple SQL calls returning part of the information you want with each call. The query above will make one SQL call to the DB and return only the data you are interested in. This is the best approach as it reduces the two bottle necks of making multiple calls to the DB and having too much data returned.
You can use following :
db.Rights.Where(right => inputRightsIds.Contains(right.Id));
They should be very similar speeds since both must enumerate the arrays the same number of times. There might be subtle differences in speed between the two depending on the input data but in general I would go with Variant 2. I think you should almost always prefer LINQ over manual enumeration when possible. Also consider using the following LINQ statement to simplify the whole search to a single line.
var matches = db.Rights.Where(r=> inputRightIds.Contains(r.Id));
...//Do stuff with matches
Not forget get all your items into memory to process list further
var itemsFromDatabase = db.Rights.Where(r => inputRightsIds.Contains(r.Id)).ToList();
Or you could even enumerate through collection and do some stuff on each item
db.Rights.Where(r => inputRightsIds.Contains(r.Id)).ToList().Foreach(item => {
//your code here
});

How to overcome null exception when using Linq Count to sort a stringlist?

I am using an array of string to sort a Linq string list according to the number of words matched from the array.
var targets = new string[] { ... }; // obtained via code
var list = = new List<string>(); // obtained via code
list = list.OrderByDescending(u => targets.Count(u.Contains)).ToList();// exception if null
The code works perfectly until it encounter a null string in list. I don't want to omit the nulls just want them sorted at the bottom of list. Is it possible to do this without using another list or intersections?
If you want to have null values be at the end of the list then you need to map them to the lowest value. In this case the key is int so use int.MinValue.
list = list
.OrderByDescending(u => u == null ? int.MinValue : targets.Count(u.Contains))
.ToList();
Rather than using this pattern though I would suggest you use List.Sort directly. The Sort method can be done in place and doesn't require the allocation of a brand new List<string>

How to access a particular data in LINQ query result?

I know, this is very simple for you guys.
Please consider the following code:
string[] str = { "dataReader", "dataTable", "gridView", "textBox", "bool" };
var s = from n in str
where n.StartsWith("data")
select n;
foreach (var x in s)
{
Console.WriteLine(x.ToString());
}
Console.ReadLine();
Supposedly, it will print:
dataReader
dataTable
right?
What if for example I don't know the data, and what the results of the query will be (but I'm sure it will return some results) and I just want to print the second item that will be produced by the query, what should my code be instead of using foreach?
Is there something like array-indexing here?
You're looking forEnumerable.ElementAt.
var secondMatch = str.Where(item => item.StartsWith("data")) //consider null-test
.ElementAt(1);
Console.WriteLine(secondMatch); //ToString() is redundant
SinceWherestreams its results, this will be efficient - enumeration of the source sequence will be discontinued after the second match (the one you're interested in) has been found.
If you find that the implicit guarantee you have that the source will contain two matches is not valid, you can use ElementAtOrDefault.
var secondMatch = str.Where(item => item.StartsWith("data"))
.ElementAtOrDefault(1);
if(secondMatch == null) // because default(string) == null
{
// There are no matches or just a single match..
}
else
{
// Second match found..
}
You could use array-indexing here as you say, but only after you load the results into... an array. This will of course mean that the entire source sequence has to be enumerated and the matches loaded into the array, so it's a bit of a waste if you are only interested in the second match.
var secondMatch = str.Where(item => item.StartsWith("data"))
.ToArray()[1]; //ElementAt will will work too
you got a few options:
s.Skip(1).First();
s.ElementAt(1);
The first is more suited for scenarios where you want X elements but after the y first elements. The second is more clear when you just need a single element on a specific location

Categories

Resources