MaxProductOfThree in C# This is my solution for the MaxProductOfThree from codility, it scores 100%, but I'm not a fan of the if if if structure it has going on. 


  Task description
  
  
  
  A non-empty array A consisting of N integers is given. The product of
  triplet (P, Q, R) equates to A[P] * A[Q] * A[R] (0 ≤ P &lt; Q &lt; R &lt; N).
  
  For example, array A such that:
  
  A[0] = -3   A[1] = 1   A[2] = 2   A[3] = -2   A[4] = 5   A[5] = 6
  contains the following example triplets:
  
  (0, 1, 2), product is −3 * 1 * 2 = −6 (1, 2, 4), product is 1 * 2 * 5 = 10 (2, 4, 5), product is 2 * 5 * 6 = 60 Your goal is to find the maximal product of any triplet.
  
  Write a function:
  
  class Solution { public int solution(int[] A); }
  
  that, given a non-empty array A, returns the value of the maximal
  product of any triplet.
  
  For example, given array A such that:
  
  A[0] = -3   A[1] = 1   A[2] = 2   A[3] = -2   A[4] = 5   A[5] = 6
  the function should return 60, as the product of triplet (2, 4, 5) is
  maximal.
  
  Write an efficient algorithm for the following assumptions:
  
  N is an integer within the range [3..100,000]; each element of array A
  is an integer within the range [−1,000..1,000].


using System;
using System.Collections.Generic;
using System.Linq;

class Solution {
    public int solution(int[] A) 
    {
        List&lt;int&gt; B = A.ToList();
        int product = 1;
        int min1 = B.Min();
        if (A.Length &gt; 3)
        {
            if (min1 &lt; 0)
            {
                B.RemoveAt(B.IndexOf(min1));
                int min2 = B.Min();
                if (min2 &lt; 0)
                {
                    int max1 = B.Max();
                    if (A.Length == 4)
                    {
                        product = min1 * min2 * max1;
                    }
                    else
                    {
                        product = Math.Max(min1 * min2 * max1, getProductOfMaximalsForMoreThanTwoElements(B));
                    }
                }
                else
                {
                    product = getProductOfMaximalsForMoreThanTwoElements(B);
                }
            }
            else
            {
                product = getProductOfMaximalsForMoreThanTwoElements(B);
            }
        }
        else
        {
            foreach (int element in B)
            {
                product *= element;
            }
        }
        return product;
    }

    private int getProductOfMaximalsForMoreThanTwoElements( List&lt;int&gt; B)
    {
        int max1 = B.Max();
        B.RemoveAt(B.IndexOf(max1));
        int max2 = B.Max();
        B.RemoveAt(B.IndexOf(max2));
        int max3 = B.Max();
        return max1 * max2 * max3;
    }
}


To be honest, the original idea was thought beforehand, but it took me a couple of tries until every test was passed, as with most of these exercises. Are there things to improve in terms of readability, logic and quality of this code?
This looks like a good attempt. Let's first start assuming we only have to deal with positive values. 

private int getProductOfMaximalsForMoreThanTwoElements( List&lt;int&gt; B)
{
    int max1 = B.Max();
    B.RemoveAt(B.IndexOf(max1));
    int max2 = B.Max();
    B.RemoveAt(B.IndexOf(max2));
    int max3 = B.Max();
    return max1 * max2 * max3;
}


This method would provide the solution. So let's review it.

Starting with a couple of style choices, method names in C# should be PascalCase. Additionally, the name of the method is vague. The method provides the product of the 3 biggest numbers: GetProductOfTop3Numbers.

B.IndexOf(B.Max()) is doing unnecessary work. Max loops over the entire list to find the maximum value, and IndexOf does it all over again. That's a waste of effort. Sadly, System.Linq doesn't provide an ArgMax method, so we will have to spin our own:"

public static int ArgMax(this IList&lt;int&gt; list) {
  var max = int.MinValue;
  var argmax = -1;
  var index = 0;
  foreach(var item in list) {
    if (item &gt; max) {
      max = item;
      argmax = index;
    }
    index++;
  }
  return argmax;
}


Here, instead of looping through the list to find the maximum value, and then search the list for that value, we can just return the index we found that biggest value at. The max is then easily retrievable through B[index].

The act of searching the list for the max, and popping it can be its own method:

public static int PopMax(List&lt;int&gt; list)
{
  var argmax = list.ArgMax();
  var max = list[argmax];
  list.RemoveAt(argmax);
  return max;
}


Our initial method would then be:

private static int GetProductOfTop3Numbers(List&lt;int&gt; list) {
  var product = 1;
  product *= PopMax(list);
  product *= PopMax(list);
  product *= PopMax(list);
  return product;
}


Or, more generic:

private static int GetProductOfTopNNumbers(List&lt;int&gt; list, int n) {
  var product = 1;
  for(var times = 0; times &lt; n; times++) {
    product *= PopMax(list);
  }
  return product;
}




Now let's return to the actual problem: negative numbers. This is what caused you problems introducing a bunch of if-statements. Let's see if we can find something for that.

We want the highest possible product. If we simply take the top 3 items from the list, we might risk missing out on very negative numbers. Take for instance the list new [] { -5, -5, -3, -1, 0, 1, 2, 3 }. In this case, the naive solution would be 1 * 2 * 3, but actually -5 * -5 * 3 has a bigger product. Note that if we consider negative numbers in our solution, it will always have to be a pair of negative numbers, to cancel out the - signs. 

This pair would then always be the two lowest entries in the list. The two most negative numbers together should provide the biggest product out of all negative numbers. The product of these can then be compared to the product of the biggest numbers, to decide which of the two sets to pick.

Furthermore, because we will never take 3 negative numbers (because the product would be negative), we can freely take the biggest number in the list. Should this number also be negative (because the input only consists of negative numbers), this is fine as well, because it would then be the least negative number, which still would result in the biggest product.

So, to recap:


Take the biggest number
Take the next two biggest numbers, or take the next two smallest numbers
Check which together has the biggest product
Done


For the mins, we need to provide a similar ArgMin and PopMin implementations. Also realise that simultaneously popping items from the list at both ends might cause troubles if this list is very short, so be sure to copy the list:

private static int GetProductOfTopNNumbers(List&lt;int&gt; list, int n) {
  var copiedList = new List&lt;int&gt;(list);
  var product = 1;
  for(var times = 0; times &lt; n; times++) {
    product *= PopMax(copiedList);
  }
  return product;
}


All in all, that leaves us with:

public int Solution(int[] A) {
  var list = A.ToList();
  var max = PopMax(list);
  return Math.Max(max * GetProductOfMinNNumbers(list, 2), max * GetProductOfTopNNumbers(list, 2));
}

public static int ArgMin(this IList&lt;int&gt; list)
{
  var min = int.MaxValue;
  var argmin = -1;
  var index = 0;
  foreach (var item in list)
  {
    if (item &lt; min)
    {
      min = item;
      argmin = index;
    }
    index++;
  }
  return argmin;
}

public static int PopMin(List&lt;int&gt; list)
{
  var argmin = list.ArgMin();
  var min = list[argmin];
  list.RemoveAt(argmin);
  return min;
}

private static int GetProductOfMinNNumbers(List&lt;int&gt; list, int n)
{
  var copiedList = new List&lt;int&gt;(list);
  var product = 1;
  for (var times = 0; times &lt; n; times++)
  {
    product *= PopMin(copiedList);
  }
  return product;
}