I have an object (KS), which holds ID and Title (which has a number as part of the Title).
All I'm trying to do is sort it into descending order. The object has:
ID Title
1 1 Outlook VPN
2 2 Outlook Access
3 4 Access VBA
4 3 Excel Automation
So when order by Title, it should read:
ID Title
3 4 Access VBA
4 3 Excel Automation
2 2 Outlook Access
1 1 Outlook VPN
The code I'm using to sort it is:
IEnumerable<KS> query = results.OrderByDescending(x => x.Title);
However, query still has the objects in the original order!
Is there something to do with having numbers at the start of Title that I'm missing?
EDIT
I've added the code from the controller for clarity:
[HttpPost]
// [ValidateAntiForgeryToken]
// id is a string of words eg: "outlook access vpn"
// I split the words and want to check the Title to see how many words appear
// Then sort by the most words found
public JsonResult Lookup(string id)
{
List<string> listOfSearch = id.Split(' ').ToList();
var results = db.KS.Where(x => listOfSearch.Any(item => x.Title.Contains(item)));
// search each result, and count how many of the search words in id are found
// then add the count to the start of Title
foreach (KS result in results)
{
result.KSId = 0;
foreach (string li in listOfSearch)
{
if (result.Title.ToLower().Contains(li.ToLower()))
{
result.KSId += 1;
}
}
result.Title = result.KSId.ToString() + " " + result.Title;
}
// sort the results based on the Title - which has number of words at the start
IEnumerable<KS> query = results.OrderByDescending(x => x.Title).ToList();
return Json(query, JsonRequestBehavior.AllowGet);
}
Here is a screenshot after query has been populated showing Titles in the order: 1, 2, 1, 1:
Model for the object if it helps is:
public class KS
{
public int KSId { get; set; }
public string KSSol { get; set; }
public string Title { get; set; }
public string Fix { get; set; }
}
As I said in a comment, put a .ToList() where you declare your results variable. That is:
var results = db.KS.Where(x => listOfSearch.Any(item => x.Title.Contains(item)))
.ToList();
If you don't do that, the foreach loop will modify objects that might not be the same as the objects you sort later, because the database query is run again each time you enumerate your IQueryable<>.
You can always just ignore the strange behavior and go the safe way:
List<KS> query = results.ToList();
query.Sort((a, b) => a.Whatever.CompareTo(b.Whatever));
return Json(query, blah);
I simple did this and it worked for me :-
var sortedOrder = Query.OrderBy(b => b.Title.Substring(b.Title.IndexOf(" ")));
All I have done is SubString the Title at the index of of the blank space when ordering the objects in the sequence, that way, the OrderBy is looking at the first character in the title rather than the number at the beginning.
Old question, but maybe this will help someone using C#. I used the following expressions to sort a list of objects based on their quantity parameter in ascending or descending order. Can modify it to compare text as the original question was concerned with.
Ascending Order:
locationMaterials.Sort((x, y) => x.Quantity.CompareTo(y.Quantity));
Descending Order:
locationMaterials.Sort((x, y) => y.Quantity.CompareTo(x.Quantity));
You are missing .ToList()
IEnumerable<KS> query = results.OrderByDescending(x => x.Title).ToList();
results.OrderByDescending(x => x.Title) is a query, and it has no data.
ToList() forces the query to be executed.
[EDIT]
My answer assumes that your results has acually not been materialized, and that that is the source of your problem.
Related
Suppose I have this table:
Image
Perimeter
a
1
b
1
b
2
d
3
e
1
I want to return the images that have relationship with only ONE perimeter.
The expected result would be images "a,d,e" because image "b" has relationship with perimeter "1" and "2".
The objective is to remove the releated image when I delete the perimeter. But if it is linked to another perimeter, I can't remove it.
How can I write this query with LINQ?
I think it would be something like this:
SELECT "ImageId"
WHERE "PerimeterId" = PerimeterId IN
(
SELECT "ImageId"
GROUP BY "ImageId"
HAVING COUNT("PerimeterId") = 1
)
but I don't know how to convert it to LINQ.
You could use a NOT EXISTS
var query = dbo.Table
.Where(t => !dbo.Table.Any(t2 => t.Image = t.Image && t.Perimeter != t2.Perimeter));
You can easily adapt this to only select the image part. But, if you are coming from SQL, thinking about "Selecting rows" based on a "HAVING()" group calculation, then you will want to look at the .SelectMany() LINQ method. This lets you "combine back together data partitioned into groups". While your needs are to only return "one from each group", it's easy to see where this can be adjusted.
This can be run in the "C# interactive window" of SSDT 2015:
struct imagePerimeter { //this might be whatever object type it is for you...
public string Image { get; set; } //a,b,b,d,e
public int Perimeter { get; set; } //1,1,2,3,1
}
Func<string, int, imagePerimeter> newIP = (i, p) => new imagePerimeter() { Image = i, Perimeter = p };
List<imagePerimeter> results = new List<imagePerimeter>() { {newIP("a",1) }
,{newIP("b",1) }
,{newIP("b",2) }
,{newIP("d",3) }
,{newIP("e",1) } };
Func<imagePerimeter, string> ipImage = (ip) => ip.Image; //the Func's "ipImage" and "newIP" could just be inlined into LINQ, but it helps to see and debug at times IMO.
var imagesWithOnePerimeter = results.GroupBy<imagePerimeter, string>(ipImage) //even in SQL, the "GROUP BY" conceptually comes first, in LINQ, it comes first in code too!
.Select(grp => new { Image = grp.Key, PerimeterCount = grp.Count(), Details = grp }) //there's probably a more technical term, but notice how we "carry forward" the original reference to [grp]
.Where(subTotals => subTotals.PerimeterCount == 1)
.SelectMany(filtered => filtered.Details.AsEnumerable())
.ToList();
Im trying to make a program that sorts objects by more then one parameters.
I need the order by to be in the same weight for all the parameters. what functions do i need to use in order to get that result?
I tried to use OrderBy() and then- ThenBy() but its ordering the first parameter first so the ordering isn't equal weight.
values = File.ReadAllLines(filepath)
.Skip(1)
.Select(v => Fund.FromCsv(v))
.OrderByDescending(x => x.sharp)
.ThenBy(x=>x.yearlychange)
.ToList();
For example you can take the stocks market, in that case i want to order the stocks by the yield in the last year but also to order by standard deviation. in that way i can get stock that have the best yield in the last year but also the best standard deviation. it wont be the best yield from all, it will be combined.
As you have been already informed, it is not really a programistic problem, more like algorithm/domain one. Nevertheless, if you already would have the algorithm, you can, of course, do it like this way. (basing on the example you present in the comment)
void Main()
{
var funds = new List<Fund>();
funds.Add(new Fund() { Age = 18, Money = 100000 });
funds.Add(new Fund() { Age = 20, Money = 101000 });
//Here is normally your code with loading it from CSV
//File.ReadAllLines(filepath)
// .Skip(1)
// .Select(v => Fund.FromCsv(v))
var value = funds.OrderBy(t => t.SortingFactor);
}
public class Fund
{
public int Age { get; set; }
public decimal Money { get; set; }
public decimal SortingFactor
{
get
{
//Here is your domain algorithm you must sort out first (your example data would be)
return Money / Age;
}
}
}
I'm not sure if I fully understand your aim but another alternative if fund is not code you can modify is an anonymous object in your order by e.g.
values = File.ReadAllLines(filepath)
.Skip(1)
.Select(v => Fund.FromCsv(v))
.OrderByDescending(x => new { x.sharp, x.yearlychange })
.ToList();
How to convert a query to bool?
I used the "ALL (x => x)" but did not give the answer I needed.
Code Line
checkItemInventory.Where(x => listCost.Contains(x.Id));
In this case, the listcost would have 2 items, I needed to check if the checkItemInventory has these 2 items.
"All items in the inventory have an id that present in listcost". listCost needs to have the same number of items as inventory (assuming Id is unique) possibly more, to stand a chance of returning true
checkItemInventory.All(x => listCost.Contains(x.Id))
"At least one item in the inventory has an id that is also in listCost". Listcost could minimally have only one id in it, to stand a chance of returning true
checkItemInventory.Any(x => listCost.Contains(x.Id))
As you can see, neither of these are what you want as you seem to be saying you want to check whether every item in listcost is also present in the inventory. This is like the top code, but the other way round ("all items in listCost are present in inventory" vs "all items in inventory are present in listcost"
I think I'd make a dictionary out of the inventory first, unless it's already something that supports a fast lookup:
var d = checkItemInventory.Select(x => new { x.Id, x.Id }).ToDictionary();
var boolResult = listCost.All(lc => d.ContainsKey(lc));
If inventory is small, you could use this approach:
listCost.All(lc => checkItemInventory.Any(cii => cii.Id == lc));
Just be mindful that internally it might do something like:
bool all = true;
foreach(lc in listCost){
bool found = false;
foreach(cci in checkItemInventory)
if(lc == cci.Id){
found = true;
break;
}
all &= found;
if(!all)
return false;
}
return true;
Which is a lot of repeated comparisons (for every item in listCost, the whole inventory is scanned), could be slow
Edit
I asked for clarification of how you store your inventory and your costs of building items. Here's one assumption I made, and how a solutio based on it might work:
Assuming your inventory has the kind of item and a count saying how many of that item the player is carrying:
class InventoryItem{
int ItemKindId { get; set;}
int CountOf { get; set; }
}
player.Inventory.Add(new InventoryItem() {
ItemKindId = Constants.WOOD, //1
CountOf = 10 //holding 10 items of wood
};
player.Inventory.Add(new InventoryItem() {
ItemKindId = Constants.STONE, //2
CountOf = 5 //holding 5 items of stone
};
Assuming you have a Recipe for making e.g. an axe, it needs 1 wood and 2 stone, but it lists them in simple order:
int[] axeRecipe = new int[] { Constants.WOOD, Constants.STONE, Constants.STONE };
Might be easiest to group the recipe:
var recipe = axeRecipe.GroupBy(item => item)
/*
now we have a grouping of the recipe[item].Key as the material and a
recipe[item].Count() of how much. The group is like a dictionary:
recipe[Constants.WOOD] = new List<int>{ Constants.WOOD };
recipe[Constants.STONE] = new List<int>{ Constants.STONE, Constants.STONE, };
A group item has a Key and a list of objects that have that key
Because my recipe was simply ints, the Key is the same number as all the
items in the list
*/
//for all items in the recipe
grp.All(groupItem =>
//does the player inventory contain any item
playerInventory.Any(inventoryItem =>
//where the material kind is the same as the recipe key (material)
inventoryItem.ItemKindId == groupItem.Key &&
//and the count they have of it, is enough to make the recipe
inventoryItem.CountOf >= groupItem.Count()
);
You can of course reduce this to a single line if you want: axeRecipe.GroupBy(...).All(...)
You could map the listCost to a list of int and then use Except() and Any() to check whether all items are contained:
bool containsAll = !listCost.Select(x => x.Id).Except(checkItemInventory).Any();
[UPDATE]
You are telling us the following:
How to convert a query to bool? I used the "ALL (x => x)" but did not give the answer I needed.
checkItemInventory.Where(x => listCost.Contains(x.Id));
In this case, the listcost would have 2 items, I needed to check if
the checkItemInventory has these 2 items.
if you need to check if there is any result then you can use:
bool hasItems = checkItemInventory.Where(x => listCost.Contains(x.Id)).Any();
if you need to count the result you can use
checkItemInventory.Where(x => listCost.Contains(x.Id)).Count();
You could use a Join to create a method based Linq query and use the results to check if the length of the list is greater than 0. Then turn that into a boolean.
var query = checkItemInventory.Join(listCost,
inventory => inventory.Id,
cost => cost.Id,
(inventory, cost) => new { id = inventory.Id });
var count = query.ToList().Count();
var b = (count > 0);
If I get it correctly, listCost can have less elements than checkItemInventory. You want to check that all elements in listCost have a corresponding element in checkItemInventory. Correct? If yes, try this:
listCost.All(x => checkItemInventory.Contains(x));
I don't know the type of these lists, so you might need to use x.id in some places
I have a City document, Site document. City can have multiple sites. Site document has the city information in it. There are about 100 city documents and 10000 site documents in RavenDB
City Document:
{
"CityCode": "NY",
"CityName": "New York"
}
Site Document:
{
"SiteName": "MOMA",
"CityCode": "NY"
}
Objective is to get a list of all cities and the number of sites for each like...
City Sites
NY 12
CH 33
BO 56
and so on....
I am doing this.
int countSites = session.Query<Site>()
.Count();
var SiteCityList = session.Query<Site>()
.Take(countSites)
.ToList()
.GroupBy(x => x.CityCode)
.OrderBy(x => x.Count())
.ToDictionary(x => x.Key, x => x.Count());
This does not give all the data in the ravendb. I get only 11 rows of count by site at any time and even the counts are not accurate. What I want is to get a list of all 100 cities and number of sites for each city (in 100s) as a list as shown above. Thanks for the help.
Use a map/reduce index like this
public class CityCodeCount : AbstractIndexCreationTask<Site, CityCodeCount.ReduceResult>
{
public class ReduceResult
{
public string CityCode { get; set; }
public int Count { get; set; }
}
public CityCodeCount()
{
Map = sites => from site in sites
select new
{
site.CityCode,
Count = 1
};
Reduce = results => from result in results
group result by result.CityCode
into g
select new
{
CityCode = g.Key,
Count = g.Sum(x => x.Count)
};
}
}
Later then, you can query it easily.
var results = documentSession.Query<CityCodeCount.ReduceResult, CityCodeCount>()
.ToList();
If you want an alternative way, you can take a look at Faceted Search
It gives you slightly more flexibility than Map/Reduce, but will only work when you want a Count of items (which you do in your case).
You've got 2 options:
Create a Map/Reduce index and query against it
Use Faceted (Aggregated) Search to query
To make a choice between the two, take into account that
A specially dedicated index is faster to query, but has a some footprint on the storage and performance for re-indexing on changed records (can be important if you need immediate consistency and wait for non-stale indexes).
Facet is simpler to use when you already have the fields covered in an existing index. I believe you understand importance of static indexes and already have some.
While using a Map/Reduce index is straightforward and already covered by Daniel's answer, I provide below an example of using Facets:
var query = DbSession.Query<Site_IndexModel, Site_ForList>();
List<FacetValue> facetResults = (await query
.AggregateBy(builder => builder.ByField(s => s.CityCode ))
.ExecuteAsync()
).Single().Value.Values;
// Go through results, where each facetResult is { Range, Count } structure
return from result in facetResults
select new { CityCode = result.Range, Count = result.Count }
where
Site_ForList is an existing index for Site collection, which includes CityCode field
Site_IndexModel is the stored structure of Site_ForList index
I want to do a search for Music instruments which has its informations Name, Category and Origin as I asked in my post.
But now I want to sort/group the result by similarity/equality to the keyword such as.
If I have the list
{ Drum, Grand Piano, Guitar, GuitarrĂ³n, Harp, Piano} << sorted by name
and if I queried "p" the result should be { Piano, Grand Piano, Harp }
but it shows Harp first because of the source list's sequence
and if I add {Grand Piano} to the list and query "piano"
the result shoud be like { Piano, Grand Piano }
or query "guitar"
it should be { Guitar, GuitarrĂ³n }
here's my code
static IEnumerable<MInstrument> InstrumentsSearch(IEnumerable<MInstrument> InstrumentsList, string query, MInstrument.Category[] SelectedCategories, MInstrument.Origin[] SelectedOrigins)
{
var result = InstrumentsList
.Where(item => SelectedCategories.Contains(item.category))
.Where(item => SelectedOrigins.Contains(item.origin))
.Where(item =>
{
if (
(" " + item.Name.ToLower()).Contains(" " + query.ToLower())
|| item.Name.IndexOf(query) != -1
)
{
return true;
}
return false;
}
)
.Take(30);
return result.ToList<MInstrument>();
}
Or the result may be like my old self-invented algorithm that I called "by order of occurence",
that is just OK to me.
And the further things to do is I need to search the Name, Category or Origin such as.
If i type "Italy" it should found Piano or something from Italy.
Or if I type "string" it should found Guitar.
Is there any way to do those things, please tell me.
Thanks in advance.
You want OrderBy / OrderByDescending --
result = InstrumentsList.
.Where(...)
.OrderByDescending(instrument =>
StringSimilarityScore(instrument.Name, searchString))
.Take(30);
As to the definition of StringSimilarityScore -- a full-on fuzzy match would be best, but you could start by quantifying the match based on the proportion of the name matched by the search string:
double StringSimilarityScore(string name, string searchString)
{
if (name.Contains(searchString))
{
return (double)searchString.Length / (double)name.Length;
}
return 0;
}
You might then want to consider the position of the search string within the name (earlier is better), for the cases where a single letter is specified -- but I'll leave that up to you. :-)