Why do the calculations not work? - c#

In the Linq code below, the count is 16 and the sum is 21 which are correct. However, the score always shows as 100. It should be 76.19. What is happening?
Also, I tried score = sum/count, but I can't seem to use the variable inside the new section. Any suggestions?
.GroupBy(g => g.YR_MNTH)
.Select(x =>
new
{
count = x.Count(),
sum = x.Sum(i=>i.SCORE >= 95? 1:0),
score = (decimal)Math.Round((decimal)(x.Sum(i => i.SCORE >= 95 ? 1 : 0) / x.Count()) * 100, 2)
});

Performing math on integers results in integers. So if you do something like this:
1 / 2
The result will not be 0.5, it will just be 0. So this:
x.Sum(i => i.SCORE >= 95 ? 1 : 0) / x.Count()
Will result in an integer. Later casting that integer to a decimal won't change its value after the fact. You need to cast the individual values before performing math on them:
(decimal)x.Sum(i => i.SCORE >= 95 ? 1 : 0) / (decimal)x.Count()

The problem is that x.Count() is an int and x.Sum(i=>i.SCORE >= 95? 1:0) is an int. An int divided by an int is an int. 21 divided by 16 in integer division is 1 which you are then multiplying by 100. You need to move your decimal cast and place it on one of you operands inside the parenthesis; like this, for example: (decimal)x.Sum(i => i.SCORE >= 95 ? 1 : 0). A decimal divided by an int will result in a decimal so you will be back in business.
On a side note performing these aggregations multiple times is not the most efficient thing to do.

Related

Compare C# string with Sql Server decimal type

I have a scenario that I have string value in c# and in SQL server the data type is decimal and requirement is that i have to filter value like we do in string which includes (startswith,endswith,contains etc) filters. I am trying like this :
customers = customers.Where(x => Convert.ToString(x.CrmCustomerNumber).Contains(value));
but it's give me error because you can't use Convert.tostring in IQuerable. I know that I can do that
customers.ToList().Where(x => Convert.ToString(x.CrmCustomerNumber).Contains(value));
but I am doing Customer.ToList() at the end after applying all filters. is there any solution of my problem?
If the numbers are of a known size, for instance if they are all six digits, then you could just query for a value range.
Example: I'm looking for all six-digit numbers that start with 123. That's the same as saying "WHERE Value BETWEEN 123000 AND 123999". So you could query .Where(x => x >= 123000 && x <= 123999).
IF the numbers aren't all a consistent size, but at least have some practical limit, you could extend this to say .Where(x => x == 123 || (x >= 1230 && x <= 1239) || (x >= 12300 && x <= 12399) || (x >=123000 && x <= 123999). etc.
With a little math, you could make this work for any number. (x >= n * 10 && x <= ((n * 10) + 9))
EndsWith can be done using modulo math.
Contains... well... you're stuck with a table scan. In that case, you might seriously consider adding a second, perhaps computed column to the table, one that stores the same value as a string, and then indexing that column. Then, use the new column to do the searches.

LINQ Grouping by Sum Value

Say I have a class like so:
public class Work
{
public string Name;
public double Time;
public Work(string name, double time)
{
Name = name;
Time = time;
}
}
And I have a List<Work> with about 20 values that are all filled in:
List<Work> workToDo = new List<Work>();
// Populate workToDo
Is there any possible way that I can group workToDo into segments where each segments sum of Time is a particular value? Say workToDo has values like so:
Name | Time
A | 3.50
B | 2.75
C | 4.25
D | 2.50
E | 5.25
F | 3.75
If I want the sum of times to be 7, each segment or List<Work> should have a bunch of values where the sum of all the Times is 7 or close to it. Is this even remotely possible or is it just a stupid question/idea? I am using this code to separate workToDo into segments of 4:
var query = workToDo.Select(x => x.Time)
.Select((x, i) => new { Index = i, Value = x})
.GroupBy(y => y.Index / 4)
.ToList();
But I am not sure how to do it based on the Times.
Here's a query that segments your data in groups where the times are near to 7, but not over:
Func<List<Work>,int,int,double> sumOfRange = (list, start, end) => list
.Skip(start)
.TakeWhile ((x, index) => index <= end)
.ToList()
.Sum (l => l.Time);
double segmentSize = 7;
var result = Enumerable.Range(0, workToDo.Count ())
.Select (index => workToDo
.Skip(index)
.TakeWhile ((x,i) => sumOfRange(workToDo, index, i)
<= segmentSize));
The output for your example data set is:
A 3.5
B 2.75
total: 6.25
B 2.75
C 4.25
total: 7
C 4.25
D 2.5
total: 6.75
D 2.5
total: 2.5
E 5.25
total: 5.25
F 3.75
total: 3.75
If you want to allow a segments to total over seven, then you could increase the segmentSize variable by 25% or so (i.e. make it 8.75).
This solution recurses through all combinations and returns the ones whose sums are close enough to the target sum.
Here is the pretty front-end method that lets you specify the list of work, the target sum, and how close the sums must be:
public List<List<Work>> GetCombinations(List<Work> workList,
double targetSum,
double threshhold)
{
return GetCombinations(0,
new List<Work>(),
workList,
targetSum - threshhold,
targetSum + threshhold);
}
Here is the recursive method that does all of the work:
private List<List<Work>> GetCombinations(double currentSum,
List<Work> currentWorks,
List<Work> remainingWorks,
double minSum,
double maxSum)
{
// Filter out the works that would go over the maxSum.
var newRemainingWorks = remainingWorks.Where(x => currentSum + x.Time <= maxSum)
.ToList();
// Create the possible combinations by adding each newRemainingWork to the
// list of current works.
var sums = newRemainingWorks
.Select(x => new
{
Works = currentWorks.Concat(new [] { x }).ToList(),
Sum = currentSum + x.Time
})
.ToList();
// The initial combinations are the possible combinations that are
// within the sum range.
var combinations = sums.Where(x => x.Sum >= minSum).Select(x => x.Works);
// The additional combinations get determined in the recursive call.
var newCombinations = from index in Enumerable.Range(0, sums.Count)
from combo in GetCombinations
(
sums[index].Sum,
sums[index].Works,
newRemainingWorks.Skip(index + 1).ToList(),
minSum,
maxSum
)
select combo;
return combinations.Concat(newCombinations).ToList();
}
This line will get combinations that sum to 7 +/- 1:
GetCombinations(workToDo, 7, 1);
What you are describing is a packing problem (where the tasks are being packed into 7-hour containers). Whilst it would be possible to use LINQ syntax in a solution to this problem, there is no solution inherent in LINQ that I am aware of.

Return the first perfect square that is greater than its integer argument

I need to write a function that returns the first perfect square that is greater than its integer argument. A perfect square is an integer that is equal to some integer squared. For example 16 is a perfect square because 16 = 4 * 4. However 15 is not a perfect square because there is no integer n such that 15 = n*n.
public static int NextPerfectSquare(int inputNumber)
{
if (inputNumber < 0) return 0;
inputNumber++;
var result = Math.Sqrt(inputNumber);
var resultnumber = (int) result;
if (resultnumber == result) return inputNumber;
resultnumber++;
return resultnumber * resultnumber;
}
Is this right?
The basic solution looks good. You may want to consider:
Should comments be added to this function? Maybe not for an exam, but worth considering.
Use consistent casing for your parameters/local variables. Consider whether they could be named more clearly.
What about boundary conditions? You've got the negative case covered, but what if inputNumber is close to int.MaxValue so that the next perfect square would be > MaxValue?
Looks right to me. Handles negative numbers, handles some arbitrary value which is not a perfect square properly, handles perfect squares properly, so I'll go with yes.
Kind of.
But I'm loathe to leave it at that because you could have verified this yourself quite easily by running some tests.
System.Console.WriteLine("-10 => {0}", NextPerfectSquare(-10));
System.Console.WriteLine("0 => {0}", NextPerfectSquare(0));
System.Console.WriteLine("1 => {0}", NextPerfectSquare(1));
System.Console.WriteLine("15 => {0}", NextPerfectSquare(15));
System.Console.WriteLine("21 => {0}", NextPerfectSquare(21));
System.Console.WriteLine("24 => {0}", NextPerfectSquare(24));
System.Console.WriteLine("36 => {0}", NextPerfectSquare(36));
System.Console.WriteLine("Max => {0}", NextPerfectSquare(int.MaxValue));
System.Console.WriteLine("Min => {0}", NextPerfectSquare(int.MinValue));
-10 => 0
0 => 1
1 => 4
15 => 16
21 => 25
24 => 25
36 => 49
Max => 1
Min => 0
So you could probably optimize it a little for bonus points?
Make it safe for large numbers. i.e. long/Int64
Make it safe from max value overflows. (try entering int.MaxValue as your input)
Seems to be working correct.
I would personally go for something like:
public static int Next(int inputNumber)
{
if (inputNumber < 0) return 0;
int perfectWidth = (int)Math.Floor(Math.Sqrt(inputNumber));
return (int)Math.Pow(perfectWidth + 1, 2);
}
as i think it shows the logic a bit clearer. But that might be my personal preferences of course ;)
You can reduce your code as
public static int NextPerfectSquare(int inputNumber)
{
if (inputNumber < 0) return 0;
var result = Math.Sqrt(inputNumber);
var resultnumber = (int) result;
resultnumber++;
return resultnumber * resultnumber;
}

c# Find value in a range using lambda

I'm trying to find an item in a list of values based on another value using a lambda expression using the Find method. In this example I'm expecting to get back -1000, but for the life of me, I just can't come up with the proper lamda expression. If that sounds confusing I hope the code and comments below explain it better.
TIA.
using System;
using System.Collections.Generic;
namespace TestingStuff {
class Program {
static void Main(string[] args) {
double amount = -200;
//The Range of values
List<MyValue> values = new List<MyValue>();
values.Add(new MyValue(-1000));
values.Add(new MyValue(-100));
values.Add(new MyValue(-10));
values.Add(new MyValue(0));
values.Add(new MyValue(100));
values.Add(new MyValue(1000));
//Find it!!!
MyValue fVal = values.Find(x => (x.Value > amount) && (x.Value < amount));
//Expecting -1000 as a result here since -200 falls between -1000 and -100
//if it were -90 I'd expect -100 since it falls between -100 and 0
if (fVal != null)
Console.WriteLine(fVal.Value);
Console.ReadKey();
}
}
public class MyValue {
public double Value { get; set; }
public MyValue(double value) {
Value = value;
}
}
}
Mmm let me put my intentions a little clearer by specifying all the expected results.
-1000 and less to -101 should give -1000
-100 to - 11 should give -100
-10 to -1 should give -10
0 to 9 should give 0
10 to 99 should give 10
100-999 should give 100
1000 or more should give 1000
This should work:
values.FindLast(x => amount >= x.Value);
You did a logical mistake ... a value can't be > -200 AND < -200 at the same time .. U need the OR expression ( "||" )
MyValue fVal = values.Find(x => (x.Value > amount) || (x.Value < amount));
But if you expect to get -1000 this expression is also wrong
MyValue fVal = values.Find(x => (x.Value < amount));
Because -1000 is SMALLER than -200
EDIT : Ok I think I missunderstood your intention. But the way you want to select your value doesn't seem logical to me. Do you want the next smaller value ?
I'm making the assumption that if you used the value +90, you'd expect 100 and not zero, as well as if you use 200, you're expecting 1000 and not 100.
MyValue fVal = values
.Where(x => amount > 0 ? x.Value > amount : x.Value < amount)
.OrderBy(x => amount > 0 ? x.Value : -x.Value).First();
Making the same assumption as Darksider Another option would be
MyValue fVal = values.Find(x => Math.Abs(x.Value) > amount && (x.Value<0 == amount<0));
of course this relies on the list already being sorted. Darksider's solution may be better if the list might not be sorted alreday.

Find the number of divisors of a number given an array of prime factors using LINQ

Given an array of prime factors of a natural number, how can I find the total number of divisors using LINQ upon the original array? I've already figured out most of this problem, but I'm having trouble with my LINQ statement.
Math Background:
The prime factors of a number are the prime integers that divide evenly into the number without a remainder. e.g. The prime factors of 60 are 2,2,3,5.
The divisors of a number are all integers (prime or otherwise) that divide evenly into the number without a remainder. The divisors of 60 are 1,2,3,4,5,6,10,12,15,20,30,60.
I am interested in finding the total number of divisors. The total number of divisors for 60 is 12.
Let's express the prime factorization using exponents:
60 = 2^2 * 3^1 * 5*1
To find the total number of divisors given the prime factorization of the number, all we have to do is add 1 to each exponent and then multiply those numbers together, like so:
(2 + 1) * (1 + 1) * (1 + 1) = 12;
That's how you find the number of divisors given the prime factorization of a number.
The Code I Have So Far:
I already have good code to get the prime factors of a number, so I'm not concerned about that. Using LINQ, I want to figure out what the total number of divisors is. I could use a few loops, but I'm trying to use LINQ (if possible).
I'm going to:
Use Distinct() to find the unique values in the array.
Use Count() to find how many time the unique values occur (this is equal to the exponent).
Use an Aggregate() function to multiply the values together.
Here's the code I have:
class Program
{
static void Main(string[] args)
{
var primeFactors = new int[] { 2, 2, 3, 5 };
Console.WriteLine(primeFactors.Distinct().PrintList("", ", "));
//Prints: 2, 3, 5
Console.WriteLine("[2]:{0} [3]:{1} [5]:{2}"
, primeFactors.Count(x => x == 2)
, primeFactors.Count(x => x == 3)
, primeFactors.Count(x => x == 5)
);
//Prints: [2]:2 [3]:1 [5]:1
\\THIS IS WHERE I HAVE TROUBLE:
Console.WriteLine(primeFactors.Distinct().Aggregate((total,next) =>
(primeFactors.Count(x => x ==next) + 1)* total));
//Prints: 8
Console.ReadLine();
}
}
Specifically, I'm having trouble with this part of code:
primeFactors.Distinct().Aggregate((total,next) =>
(primeFactors.Count(x => x ==next) + 1)* total)
Since the numbers in my array are not stored in the form of x^n, but rather in the form of n instances of x in the array, my thinking is to use Count() to find what n ought to be on a distinct array of x. The Aggregate function is intended to iterate through each distinct item in the array, find its Count + 1, and then multiply that by the total. The lambda expression in Count is supposed to use each distinct number as a parameter (next).
The above code should return 12, but instead it returns 8. I have trouble "stepping through" LINQ in debug mode and I can't figure out how I might better write this.
Why doesn't that portion of my code return the correct number of divisors as I expect? Is there a different (better) way to express this using LINQ?
Try this:
int[] factors = new int[] { 2, 2, 3, 5 };
var q = from o in factors
group o by o into g
select g.Count() + 1;
var r = q.Aggregate((x, y) => x * y);
The specific problem with your suggested query is that your aggregate call fails to count the very first element (not to mention doesn't increment the count by 1). What it erroneously does is take the first factor and multiplies its value instead of its count + 1 with the next one.
If I understand what you're looking to do, you may want to use GroupBy() instead.
var primeFactors = new int[]{ 2, 2, 3, 5 };
var numFacs = primeFactors.GroupBy(f => f, f => f, (g, s) => s.Count() + 1)
.Aggregate(1, (x, y) => x * y);

Categories