I am tasked to transform an Excel VBA to a web-based application. I am using Web Forms. I am stuck with a long Excel formula but I have converted half of the long formula into C#.
Here is the Excel formula:
IF(AND(D7="u", H7/F7>1), 0, INDEX(Scoring!$O$8:$O$10, SUMPRODUCT(--(E28 <= Scoring!$N8:$N10),--(E28 >= Scoring!$N8:$N10), ROW(INDIRECT("'Scoring'!$M1:$M3"))))
While here is my progress on the C# method. I need help on the ELSE part:
IF (variable = "u" && ((CurrentValue / AcceptedValue) > 1)){
return 0;
ELSE {
// the INDEX clause on the Excel Formula
}
O8 - O10 values are {2, 1, 0}
N8 - N10 values are {1, 1.5, 9999999999}
E28 is 0
I have also got the result of the remainder of the formula piece by piece. I just need guidance on how to put it in C#.
=INDEX(Scoring!$O$8:$O$10, SUMPRODUCT({1,1,1}, {0,0,0}, 1))
The double minus(--) made it return 0/1 instead of true/false. ROW(INDIRECT($M1-$M3)) is equivalent to 1.
I could use some help on transforming the Excel formula into C# code.
Here is some C# code to replicate this part of your formula:
INDEX(Scoring!$O$8:$O$10, SUMPRODUCT(--(E28 <= Scoring!$N8:$N10),--(E28 >= Scoring!$N8:$N10), ROW(INDIRECT("'Scoring'!$M1:$M3")))
In the code I've had to assign the array {1, 1, 1} for ROW(INDIRECT("'Scoring'!$M1:$M3")). There is some ambiguity in your question around the action of SUMPRODUCT. According to support.office.com
Multiplies corresponding components in the given arrays, and returns the sum of those products.
But some of your detail in the question makes me think you are thinking that each array get summed up and then those results get multiplied together. Anyway, check out the comments in the code below to follow the logic:
// inputs
double[] o8o10 = new double[] { 2, 1, 0 }; // Scoring!$O$8:$O$10
double[] n8n10 = new double[] { 1, 1.5, 9999999999 }; // Scoring!$N8:$N10
double e28 = 0; // E28
// entries to SUMPRODUCT
List<int> test1 = new List<int>();
List<int> test2 = new List<int>();
Array.ForEach(n8n10, x => { test1.Add((e28 <= x) ? 0 : 1); });
Array.ForEach(n8n10, x => { test2.Add((e28 >= x) ? 0 : 1); });
// ROW(INDIRECT("'Scoring'!$M1:$M3")) should be an array !
List<int> test3 = new List<int> { 1, 1, 1 };
// evalue SUMPRODUCT
int sumProductResult = 0;
for (var i=0; i<test1.Count; i++)
{
sumProductResult += test1[i] * test2[i] * test3[i];
}
// evalute INDEX
double indexResult = 0;
indexResult = o8o10[sumProductResult];
// output
Console.WriteLine(indexResult);
Console.ReadKey();
The output for me is 2 because that is the 0th element of the o8o10 array. We get 0 for the array index because
=SUMPRODUCT({1, 1, 1}, {0, 0, 0}, {1, 1, 1})
resolves to:
=SUM(1*0*1, 1*0*1, 1*0*1}
Which gives 0.
HTH
ROW(INDIRECT("'Scoring'!$M1:$M3")) actually evaluates to {1; 2; 3} and
--(E28 <= Scoring!$N8:$N10),--(E28 >= Scoring!$N8:$N10) is --(E28 = Scoring!$N8:$N10)
so the SUMPRODUCT formula is just:
(E28 = Scoring!$N8) * 1 + (E28 = Scoring!$N9) * 2 + (E28 = Scoring!$N10) * 3
and in C#:
double[] n8n10 = { 1, 2, 3 }; double e28 = 2;
double sumProduct = n8n10.Select((d, i) d == e28 ? i + 1 : 0).Sum();
But! because Scoring!$O$8:$O$10 is only 3 cells and the SUMPRODUCT can in theory result in more than 3, the actual aim of the INDEX SUMPRODUCT part seems to be something like this:
double[] o8o10 = { 4, 5, 6 }, n8n10 = { 1, 2, 3 };
double e28 = 2, result = o8o10[0]; // INDEX(array, 0) results in array
if (n8n10[1] == e28) result = o8o10[1];
else if (n8n10[2] == e28) result = o8o10[2];
Related
There is a list of short. The values of it doesn't matter like:
List<short> resultTemp = new List<short>{1,2,3,4,5,6,7,8,9...};
This code should reduse the result list count by removing each Nth item from it.
Example 1:
List<short>{1,2,3,4,5,6,7,8,9,10}.Count == 10;
var targetItemsCount = 5;
result should be {1,3,5,7,9} and result.Count should be == 5
Example 2:
List<short>{1,2,3,4,5,6,7,8,9}.Count == 9;
var targetItemsCo:nt = 3;
result should be {1,4,7} and result.Count should be == 3
But it should stop to remove it, somewhere for make result count equal targetItemsCount (42 in this code, but its value else doesn't matter).
The code is:
var currentItemsCount = resultTemp.Count;
var result = new List<short>();
var targetItemsCount = 42;
var counter = 0;
var counterResettable = 0;
if (targetItemsCount < currentItemsCount)
{
var reduceIndex = (double)currentItemsCount / targetItemsCount;
foreach (var item in resultTemp)
{
if (counterResettable < reduceIndex ||
result.Count + 1 == currentItemsCount - counter)
{
result.Add(item);
counterResettable++;
}
else
{
counterResettable = 0;
}
counter++;
}
}
And the resault.Count in this example equals 41, but should be == targetItemsCount == 42;
Ho do I remove each N item in List untill List.Count more then target value with C#?
If my understanding is correct:
public static void run()
{
var inputs =
new List<Input>{
new Input{
Value = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },`
TargetCount = 5, ExpectedOutput= new List<int>{1,3,5,7,9}
},
new Input{
Value = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
TargetCount = 3, ExpectedOutput= new List<int>{1,4,7}
},
};
foreach (var testInput in inputs)
{
Console.WriteLine($"# Input = [{string.Join(", ", testInput.Value)}]");
var result = Reduce(testInput.Value, testInput.TargetCount);
Console.WriteLine($"# Computed Result = [{string.Join(", ", result)} ]\n");
}
}
static List<int> Reduce(List<int> input, int targetItemsCount)
{
while (input.Count() > targetItemsCount)
{
var nIndex = input.Count() / targetItemsCount;
input = input.Where((x, i) => i % nIndex == 0).ToList();
}
return input;
}
class Input
{
public List<int> ExpectedOutput;
public List<int> Value;
public int TargetCount;
}
Result :
Input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Computed Result = [1, 3, 5, 7, 9 ]
Input = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Computed Result = [1, 4, 7 ]
To guarantee you get the expected number of selected items:
double increment = Convert.ToDouble(resultTemp.Count) / targetItemsCount;
List<short> result = Enumerable.Range(0, targetItemsCount).
Select(x => resultTemp[(int)(x * increment)]).
ToList();
Note that in the following case
List<short>{1,2,3,4,5,6,7,8,9}.Count == 9;
var targetItemsCount = 6;
The result will be [1, 2, 4, 5, 7, 8], i.e. rounding the index down when needed
Also, you'll need to add validation (targetItemsCount > 0, targetItemsCount < resultTemp.Count...)
Link to Fiddle
Give this a try:
var resultTemp = Enumerable.Range(1, 9).ToList();
var targetItemsCount = 3;
var roundingError = resultTemp.Count % targetItemsCount;
var reduceIndex = (resultTemp.Count - roundingError) / targetItemsCount;
List<int> result;
if (reduceIndex <= 1)
result = resultTemp.Take(targetItemsCount).ToList();
else
result = resultTemp.Where((a, index) => index % reduceIndex == 0).Take(targetItemsCount).ToList();
Tried it with your given example, also gave 42 a spin with a list of 1 to 100 it will remove every 2nd item till it reaches 42, so the last entry in the list would be 83.
As I said, give it a try and let me know if it fits your requirement.
im trying to multiply each element in three different arrays by 2 with a loop but im having trouble. im really new at this so please excuse any obvious mistakes lol im not even sure ive im using the right kind of loop but heres what i have so far:
int[] firstArray = new int[] { 1, 2, 5, 6, 9 };
int[] secondArray = new int[] { 12, 3, 8, 20, 7 };
int[] thirdArray = new int[] { 2, 4, 6, 8, 10, 12 };
foreach(new int [5] in firstArray)
{
int newArray1= firstArray.Length * 2;
Console.WriteLine(newArray1);
}
i want it to print out the first new array as "2, 4, 10, 12, 18" in the console but when i run it, i get the error type and identifier are both required in a foreach statement.
any help would be greatly appreciated!
Do this with Linq
int[] resultFirstArray = firstArray.Select(r=> r * 2).ToArray();
int[] resultSecondArray = secondArray.Select(r=> r * 2).ToArray();
int[] resultThirdArray = thirdArray.Select(r=> r * 2).ToArray();
Or you can use Array.ConvertAll
Array.ConvertAll converts an entire array. It converts all elements in one array to another type.
var resultFirstArray = Array.ConvertAll(firstArray, x => 2 * x);
var resultSecondArray = Array.ConvertAll(secondArray, x => 2 * x);
var resultThirdArray = Array.ConvertAll(thirdArray, x => 2 * x);
If you just want to show the doubled values:
foreach(int value in firstArray)
{
Console.WriteLine(2 * value);
}
If you want to double the values in the array, then:
for(int i = 0 ; i < firstArray.Length ; i++)
{
firstArray[i] *= 2;
}
Then perhaps to show those values:
foreach(int value in firstArray)
{
Console.WriteLine(value);
}
If you want to create a new array with the values doubled:
var doubledArray = Array.ConvertAll(firstArray, x => 2 * x);
And to output those values:
foreach(int value in doubledArray)
{
Console.WriteLine(value);
}
I'm trying to reverse engineer a calculation that was done on an old program but can't quite get it. I need a count of how many values are in the top 27%, middle 46%, and bottom 27%.
I have the following data sets with eleven values in each and what the program yields in regards to the percentages and the number of values that fall into those percentiles.
Upper 27%: 4, Middle 46%: 4, Lower 27%: 3
values: 8,9,10,11,11,11,11,12,12,12,13
Upper 27%: 5, Middle 46%: 4, Lower 27%: 2
values: 2,3,4,4,4,4,5,5,5,5,5
Upper 27%: 2, Middle 46%: 8, Lower 27%: 1
values: 2,4,4,4,4,4,4,4,4,5,5
Upper 27%: 2, Middle 46%: 6, Lower 27%: 3
values: 13,17,17,18,19,19,19,21,21,23,24
I have found formulas such as (n * p) where n is the number of values and p is the percentile but it doesn't seem to work across all of these data sets to give the same results. I'm a little lost and haven't found anything that produces the results here.
I have tested code that I have found on the internet but none have worked on the different data sets.
Code sample that I tried:
internal static double percentile(double[] sortedData, double p)
{
if (p >= 100.0d) return sortedData[sortedData.Length - 1];
double position = (double)(sortedData.Length + 1) * p / 100.0;
double leftNumber = 0.0d, rightNumber = 0.0d;
double n = p / 100.0d * (sortedData.Length - 1) + 1.0d;
if (position >= 1)
{
leftNumber = sortedData[(int)System.Math.Floor(n) - 1];
rightNumber = sortedData[(int)System.Math.Floor(n)];
}
else
{
leftNumber = sortedData[0]; // first data
rightNumber = sortedData[1]; // first data
}
if (leftNumber == rightNumber)
return leftNumber;
else
{
double part = n - System.Math.Floor(n);
return leftNumber + part * (rightNumber - leftNumber);
}
}
Is there a formula or a name for what I am trying to do? Am I on the right track with percentile ranks?
You're on the right path. This is indeed the percentile rank formula. My initial thought was to get the value at the 27th percentile (which looking at your code it looks like you started down this path as well) and figure out how many values were greater than or less than; but the values you provided don't support these numbers very well.
Therefore the approach I took was to calculate the percentile rank of each number and put it into a count if they matched the percentile you have above. This seems to be the approach they took.
Formula (check out this website for more info):
PR% = L + ( 0.5 x S ) / N
Where,
L = Number of below rank,
S = Number of same rank,
N = Total numbers.
Code:
var lower = 0;
var middle = 0;
var upper = 0;
// var values = new int[] { 8, 9, 10, 11, 11, 11, 11, 12, 12, 12, 13 };
// var values = new int[] { 2, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5 };
// var values = new int[] { 2, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5 };
var values = new int[] { 13, 17, 17, 18, 19, 19, 19, 21, 21, 23, 24 };
var n = values.Length;
foreach(var i in values)
{
var pr = ((values.Count(v => v < i) + (.5 * values.Count(v => v == i))) / n);
if (pr < .27)
lower += 1;
else if (pr > .73)
upper += 1;
else
middle += 1;
}
Console.WriteLine("Upper: " + upper);
Console.WriteLine("Middle: " + middle);
Console.WriteLine("Lower: " + lower);
given:
int[] myArray = new int[]{-8, -17, 12, 8, 16, -3, 7, 3};
Find all values in array closest to 0.
Currently using another code found on the site but it only tells me 1 value.
int nearest = myArray.Select(p => new {Value = p, Difference = Math.Abs(p - searchValue)}).OrderBy(p => p.Difference).First().Value;
In the current case both -3 and 3 are closest to 0 but since -3 comes first it only outputs -3.
Is there another way to do this where it finds all values instead of just First()?
Just to add to this. If you want to do this using O(n) complexity without sorting then you can do it this way :
public List<int> GetClosestNumbers(int searchVal)
{
int[] myArray = new int[] { -8, -17, 12, 8, 16, -3, 7, 3 };
int minimumDist = int.MaxValue;
List<int> output = new List<int>();
for (int i = 0; i < myArray.Length; i++)
{
var currentDistance = Math.Abs(myArray[i] - searchVal);
if (currentDistance < minimumDist)
{
minimumDist = currentDistance;
output.Clear();
output.Add(myArray[i]);
}
else if (minimumDist == currentDistance)
{
output.Add(myArray[i]);
}
}
return output;
}
Use a grouping on the lowest absolute value
int[] result = myArray
.OrderBy(i => Math.Abs(i - searchValue))
.GroupBy(i => Math.Abs(i - searchValue))
.First()
.ToArray();
Suppose I have this number list:
List<int> = new List<int>(){3,5,8,11,12,13,14,21}
Suppose that I want to get the closest number that is less than 11, it would be 8
Suppose that I want to get the closest number that is greater than 13 that would be 14.
The numbers in list can't be duplicated and are always ordered. How can I write Linq for this?
with Linq assuming that the list is ordered I would do it like this:
var l = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
var lessThan11 = l.TakeWhile(p => p < 11).Last();
var greaterThan13 = l.SkipWhile(p => p <= 13).First();
EDIT:
As I have received negative feedback about this answer and for the sake of people that may see this answer and while it's accepted don't go further, I explored the other comments regarding BinarySearch and decided to add the second option in here (with some minor change).
This is the not sufficient way presented somewhere else:
var l = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
var indexLessThan11 = ~l.BinarySearch(10) -1;
var value = l[indexLessThan11];
Now the code above doesn't cope with the fact that the value 10 might actually be in the list (in which case one shouldn't invert the index)! so the good way is to do it:
var l = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
var indexLessThan11 = l.BinarySearch(10);
if (indexLessThan11 < 0) // the value 10 wasn't found
{
indexLessThan11 = ~indexLessThan11;
indexLessThan11 -= 1;
}
var value = l[indexLessThan11];
I simply want to note that:
l.BinarySearch(11) == 3
//and
l.BinarySearch(10) == -4;
Use Array.BinarySearch - no need for LINQ or visiting on average half the elements to find your target.
There are also a variety of SortedXXX classes that may be suitable for what you're doing [that will have such efficient O(log N) searches built-in]
You can do this using a binary search. If your searching for 11, well obviously you'll get the index your after. If you search for 10 and use the bitwise complement of the result, you'll get the closest match.
List<int> list = new List<int>(){3,5,8,11,12,13,14,21};
list.Sort();
int index = list.BinarySearch(10);
int found = (~index)-1;
Console.WriteLine (list[found]); // Outputs 8
The same goes searching in the other direction
int index = list.BinarySearch(15);
Console.WriteLine("Closest match : " + list[+~index]); // Outputs 21
Binary searches are also extremely fast.
closest number below 11:
int someNumber = 11;
List<int> list = new List<int> { 3, 5, 8, 11, 12, 13, 14, 21 };
var intermediate = from i in list
where i < someNumber
orderby i descending
select i;
var result = intermediate.FirstOrDefault();
closest number above 13:
int someNumber = 13;
List<int> list = new List<int> { 3, 5, 8, 11, 12, 13, 14, 21 };
var intermediate = from i in list
where i > someNumber
orderby i
select i;
var result = intermediate.FirstOrDefault();
This is my answer
List<int> myList = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
int n = 11;
int? smallerNumberCloseToInput = (from n1 in myList
where n1 < n
orderby n1 descending
select n1).First();
int? largerNumberCloseToInput = (from n1 in myList
where n1 > n
orderby n1 ascending
select n1).First();
var list = new List<int> {14,2,13,11,5,8,21,12,3};
var tested = 11;
var closestGreater = list.OrderBy(n => n)
.FirstOrDefault(n => tested < n); // = 12
var closestLess = list.OrderByDescending(n => n)
.FirstOrDefault(n => tested > n); // = 8
if (closestGreater == 0)
System.Diagnostics.Debug.WriteLine(
string.Format("No number greater then {0} exists in the list", tested));
if (closestLess == 0)
System.Diagnostics.Debug.WriteLine(
string.Format("No number smaler then {0} exists in the list", tested));
Here is my way hope this helps somebody!
List<float> list = new List<float> { 4.0f, 5.0f, 6.0f, 10.0f, 4.5f, 4.0f, 5.0f, 6.0f, 10.0f, 4.5f, 4.0f, 5.0f, 6.0f, 10.0f };
float num = 4.7f;
float closestAbove = list.Aggregate((x , y) => (x < num ? y : y < num ? x : (Math.Abs(x - num)) < Math.Abs(y - num) ? x : y));
float closestBelow = list.Aggregate((x , y) => (x > num ? y : y > num ? x : (Math.Abs(x - num)) < Math.Abs(y - num) ? x : y));
Console.WriteLine(closestAbove);
Console.WriteLine(closestBelow);
This means you dont have to order the list
Credit: addapted from here: How to get the closest number from a List<int> with LINQ?
The Expanded Code
float closestAboveExplained = list.Aggregate((closestAbove , next) => {
if(next < num){
return closestAbove;
}
if(closestAbove < num){
return next;
}
else{
if(Math.Abs(closestAbove - num) < Math.Abs(next - num)){
return closestAbove;
}
}
return next;
});
You can use a query for this such as:
List<int> numbers = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
List<int> output = (from n in numbers
where n > 13 // or whatever
orderby n ascending //or descending
select n).ToList();