Get the Count and Name - c#

I have a class called Cars which has two properties i.e. Count and Name.
public class Cars
{
public string Name { get; set; }
public int Count{ get; set; }
}
I am trying to unit test my repository layer and am not able to get the value of Count correctly.
Here is my unit test
public void GetCarStats()
{
var mockRepo = new VehicleRepository();
var result = mockRepo.GetCarStats(Guid.Parse("9F733662-FP4E-69DC-AX600-A4C250F9E166"));
Assert.NotEmpty(result);
Assert.Equal(1, result.Count);
var cars= result.Where(x => x.Count > 0).Select( v => v.Count);
Assert.Equal(6, cars);//This statement is failing
}
Could anyone help me?

This statement
var cars= result.Where(x=>x.Count>0).Select(v=>v.Count);
will give you an IEnumerable<int> and you are trying to compare it with a single number 6 , this should fail.
Depending on what you need, you can get the First element from your collection and compare it with 6 or you can use Sum to get the total number of count.
var cars= result.Where(x=>x.Count>0).Select(v=>v.Count).FirstOrDefault();
Assert.Equal(6,cars);//This statement is failing
If you are interested in total number of records returned against your condition count > 0 then use Count instead of Select(v=> v.Count) like:
var cars = result.Where(x => x.Count > 0).Count();
or
var cars = result.Count(x=> x.Count > 0);

You are comparing a list of int to and int. Try this for sum of count:
var cars= result.Where(x=>x.Count>0).Sum(v=>v.Count);
Assert.Equal(6,cars);
and this for amount of results that have more than 0 cars:
var cars= result.Where(x=>x.Count>0).Select(v=>v.Count);
Assert.Equal(6,cars.Count());
Not sure which one you are looking for. This assumes you have setup the data for your test initially, otherwise the list will be empty anyway.

Related

Group objects list by Dictionary value

I've list of object which looks like below
public class test
{
public int ID{ get; set; }
public string Name { get; set; }
public Dictionary<string, string> SampleXML { get; set; }
}
I want to group the list based on values in 'SampleXML'. e.g. It contains values like price. I want to group List based on price.
How to implement the same. I tried below code but it just seperates the list by key. I need unique records.
Dictionary<string, List<test>> result = Obj.LstTest
.GroupBy(x => x.SampleXML["Price"])
.ToDictionary(g => g.Key,
g => g.ToList());
The above code generates the list based on price. e.g. If there are 6 records with 3 different prices, above code return 3 results,but again the list contains two records each.
EDIT
If the price is blank then it needs to be ignored in grouping that is if there are 3 records and all doesn't have prices then list will contain 3 item(as it is).
Any help is appreciated.
Edit: Based on Vicky S edit
To still have the result as a List of test:
List<test> list = new List<test>();
List<test> result = new List<test>();
result = list.GroupBy(x => x.SampleXML["Price"]).Select(g=>g.FirstOrDefault());
Edit: To include tests that have SampleXML with empty Price value or no Price Key. Do the following:
First we need to separate tests with "No Price" and include them to our final result, from test that have "Price" value to group them.
var emptyPrice = list.Where(l => !l.SampleXML.ContainsKey("Price") || l.SampleXML["Price"] == string.Empty).ToList();
var withoutEmptyPrice = list.Where(l => l.SampleXML.ContainsKey("Price") && !string.IsNullOrEmpty(l.SampleXML["Price"]));
Then we will group tests with "Price" value as my first answer.
var resultWithEmptyPrice = withoutEmptyPrice.GroupBy(x => x.SampleXML["Price"]).Select(g => g.FirstOrDefault()).ToList();
Finally, we will add the "No Price" tests to our result.
resultWithEmptyPrice.AddRange(emptyPrice);

Filter c# list using timestamp, Take first record of each 5 seconds

I have a scenario like to filter the record based on timings.That is first record in a range of 5 seconds.
Example :
Input data :
data timings
1452 10:00:11
1455 10:00:11
1252 10:00:13
1952 10:00:15
1454 10:00:17
1451 10:00:19
1425 10:00:20
1425 10:00:21
1459 10:00:23
1422 10:00:24
Expected output
1452 10:00:11
1454 10:00:17
1459 10:00:23
I have tried to group the data based on timings like below
listSpacialRecords=listSpacialRecords.GroupBy(x => x.timings).Select(x => x.FirstOrDefault()).ToList();
But using this i can only filter the data using same time.
It hope someone can help me to resolve this
List contain huge data, so is there any way rather than looping through list ?
This works for me:
var results =
source
.Skip(1)
.Aggregate(
source.Take(1).ToList(),
(a, x) =>
{
if (x.timings.Subtract(a.Last().timings).TotalSeconds >= 5.0)
{
a.Add(x);
}
return a;
});
I get your desired output.
This should do (assuming listSpacialRecords is in order)
var result = new List<DateTime>();
var distance = TimeSpan.FromSeconds(5);
var pivot = default(DateTime);
foreach(var record in listSpacialRecords)
{
if(record.timings > pivot)
{
result.Add(record.timings); // yield return record.timings; as an alternative if you need defered execution
pivot = record.timings +distance;
}
}
If not, easiest but maybe not the most efficient way would be to change the foreach a littlebit
foreach(var time in listSpacialRecords.OrderBy(t=>t))
Doing this only using Linq is possible, but wont benefit readability.
assuming your class looks something like this:
public class DataNode
{
public int Data { get; set; }
public TimeSpan Timings { get; set; }
}
I wrote an extension method:
public static IEnumerable<DataNode> TimeFilter(this IEnumerable<DataNode> list, int timeDifference )
{
DataNode LastFound = null;
foreach (var item in list.OrderByDescending(p=> p.Timings))
{
if (item.Timings > LastFound?.Timings.Add(new TimeSpan(0,0,timeDifference)))
{
LastFound = item;
yield return item;
}
}
}
This can then be used like this
var list = new List<DataNode>();
var result = list.TimeFilter(5);
Something like this approach may work, using the % Operator (Modulo)
Assumptions
The list is in order
You don't care if it skips missing seconds
There is always a first element
And this is only within a 24 hour period
Note : Totally untested
var seconds = listSpacialRecords
.First() // get the first element
.Timmings
.TimeOfDay // convert it to TimeSpan
.TotalSeconds; // get the total seconds of the day
var result = listSpacialRecords
.Where(x => (x.Timmings
.TimeOfDay
.TotalSeconds - seconds) % 5 == 0)
// get the difference and mod 5
.ToList();

LINQ Expression to Select objects by string property with maximum count of objects in its queue property without duplicates

I have a queue of Record objects as follows:
public class Record
{
public string TypeDesc { get; set; }
public Queue<Total> Totals { get; set; }
etc.....
}
I'm having trouble writing a LINQ expression to extract a subset that has only one of each TypeDesc but within each TypeDesc the one with the most Total objects in the Totals queue.
I'm not sure it matters but there is only one TypeDesc that has Total objects in the Totals queue property. All others the queue is empty. There are about 8 unique TypeDesc values.
Here's my attempt but the totals property is not available on "s".
var records = Records.Select(c => c.TypeDesc).Where(s => s.Totals.Count).Max().Distinct();
group the records by their TypeDesc property
For each group, select the one with the highest Totals.Count.
records.GroupBy(r => r.TypeDesc)
.Select(
g => g.Aggregate((acc, current) => current.Totals.Count > acc.Totals.Count
? current
: acc));
For complex queries like these, it's best to break the logic down a bit, to make the code more readable:
Func<IEnumerable<Record>, Record> mostTotals =
group => group.Aggregate(
(acc, current) => current.Totals.Count > acc.Totals.Count
? current
: acc);
var records = records.GroupBy(r => r.TypeDesc)
.Select(mostTotals);
Step 2 is achieved by using Aggregate, which iterates through the records in that group, and uses an "accumulator" to keep track of the record with the highest Totals.Count at each iteration.
To simplify, the aggregation function is equivalent to this:
//for each group
Record acc = null;
foreach(var current in group)
acc = current.Totals.Count > acc.Totals.Count
? current
: acc;

Sorting a list of objects with OrderByDescending

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.

Storing the list items into a variable after filtering by using linq

I got a list of items, want to filter the list based on column distinct value(i.e based on Level) and also after filtering need to get the count and store them as an int variable.
Can anyone please help me.
**List**
Public Class Totalitems
{
public string ItemName;
public string ItemId;
public string ItemGroup;
public int Level;
}
Id= "123asd";
List<Totalitems> l_items = this.getslist(Id);
/*How to filter based on distinct level */
/* var filteredItems = (
from p in l_items
select p.Level)
.Distinct(); */
**Finally:**
//Stores the elements contained in the List into a variable
int totalItemsafterFiltering = l_FilteredItems.Count;
You want to use GroupBy for this task:
var numberOfDifferentLevels = l_items.GroupBy(x => x.Level).Count();
GroupBy is especially useful, if you want to do something with the actual elements in the group. For example, you might want to know how many items per level there are:
var itemsPerLevel = l_items.GroupBy(x => x.Level)
.Select(x => new { Level = x.Key,
NumberOfItems = x.Count() });
Another approach when you really only care about the number of distinct levels, is the following:
var numberOfDifferentLevels = l_items.Select(x => x.Level).Distinct().Count();

Categories