HackerRank: Electronics ShopMaximizing XOR HackerRank challengeConverting from binary to unaryHackerrank Sum vs XoRHackerRank - Matrix RotationHackerrank: Sherlock and anagramHackerrank: Prefix neighborsHackerrank: Gridland MetroHackerrank: Kindergarten AdventuresHackerrank: Max ScoreFind out how many laptops Molly can buy based on available money, array of quantities and array of costs

Can I capture stereo IQ signals from WebSDR?

Can a continent naturally split into two distant parts within a week?

Is this a Lost Mine of Phandelver Plot Hole?

Should you avoid redundant information after dialogue?

Absconding a company after 1st day of joining

Why is dry soil hydrophobic? Bad gardener paradox

How are "soeben" and "eben" different from one another?

(algebraic topology) question about the cellular approximation theorem

Why does the Earth have a z-component at the start of the J2000 epoch?

Can a pizza stone be fixed after soap has been used to clean it?

Is a public company able to check out who owns its shares in very detailed format?

Filtering fine silt/mud from water (not necessarily bacteria etc.)

Basic example of a formal affine scheme, functorial point of view

Add region constraint to Graphics

How do I define this subset using mathematical notation?

Why use null function instead of == []

How did John Lennon tune his guitar

Alternatives to using writing paper for writing practice

Why is "dark" an adverb in this sentence?

How can we better understand multiplicative inverse modulo something?

What is the closed form of the following recursive function?

What is the English equivalent of 干物女 (dried fish woman)?

Possible isometry groups of open manifolds

Remove intersect line for one circle using venndiagram2sets



HackerRank: Electronics Shop


Maximizing XOR HackerRank challengeConverting from binary to unaryHackerrank Sum vs XoRHackerRank - Matrix RotationHackerrank: Sherlock and anagramHackerrank: Prefix neighborsHackerrank: Gridland MetroHackerrank: Kindergarten AdventuresHackerrank: Max ScoreFind out how many laptops Molly can buy based on available money, array of quantities and array of costs






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








7












$begingroup$


Challenge from Hacker Rank -




Monica wants to buy a keyboard and a USB drive from her favorite electronics store. The store has several models of each. Monica wants to spend as much as possible for the items, given her budget.



Given the price lists for the store's keyboards and USB drives, and Monica's budget, find and print the amount of money Monica will spend. If she doesn't have enough money to both a keyboard and a USB drive, print -1 instead. She will buy only the two required items.




For example - with a budget of 10, two keyboards costing 3,1 & finally three drives available costing 5,2,8, the answer should be 9 as she is only able to purchase the keyboard for 3 and a drive for 5.



More test cases are available on HackerRank.



I've attempted to solve this logically and with good performance in mind. I'm not sure if I should be happy with my solution. I would appreciate any feedback.



My solution (which works) or my GitHub repo -



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

namespace ElectronicsShop

class Program

static void Main(string[] args)


Console.WriteLine(GetMoneySpent(new int[] 3, 1 , new int[] 5, 2, 8 , 10));
Console.WriteLine(GetMoneySpent(new int[] 5, new int[] 4 , 5));
Console.ReadLine();


static int GetMoneySpent(int[] keyboards, int[] drives, int budget)

if (budget == 0)
return -1;

// sort the two arrays so the highest values are at the front
keyboards = SortArrayDescending(keyboards);
drives = SortArrayDescending(drives);

// delete any that are over our budget
var affordableKeyboards = GetAffordableItems(keyboards, budget);
var affordableDrives = GetAffordableItems(drives, budget);

// make a list to contain the combined totals
var combinedTotals = new List<int>();

foreach (var keyboard in keyboards)

foreach (var drive in drives)

combinedTotals.Add(keyboard + drive);



// sort the list & delete anything over budget
combinedTotals.Sort();
combinedTotals.Reverse();
combinedTotals.RemoveAll(n => n > budget);

return combinedTotals.Count == 0 ? -1 : combinedTotals[0];


static int[] SortArrayDescending(int[] array)

Array.Sort(array);
Array.Reverse(array);

return array;


static int[] GetAffordableItems(int[] array, int budget)

return array.Where(n => n < budget).ToArray();












share|improve this question











$endgroup$







  • 2




    $begingroup$
    Regarding the sample in the problem description, if Monica buys items costing $3$ and $5 ,$ wouldn't that just be $8 ?$ I think you meant that she buys the keyboard for $1$ and the USB-drive for $8 ,$ yielding the total of $9 .$
    $endgroup$
    – Nat
    15 hours ago











  • $begingroup$
    @Nat good spot! Yes you're right - I've fixed that now. Turns out I can't do basic Maths :)
    $endgroup$
    – Webbarr
    14 hours ago










  • $begingroup$
    Before going for good code you should go for a good algorithm. It seems your algorithm has $O(nk)$ runtime and memory. The problem can however be solved in $O(nlog n+klog k)$ time with $O(n+k)$ memory
    $endgroup$
    – Hagen von Eitzen
    3 hours ago











  • $begingroup$
    What you may and may not do after receiving answers
    $endgroup$
    – Jamal
    2 hours ago

















7












$begingroup$


Challenge from Hacker Rank -




Monica wants to buy a keyboard and a USB drive from her favorite electronics store. The store has several models of each. Monica wants to spend as much as possible for the items, given her budget.



Given the price lists for the store's keyboards and USB drives, and Monica's budget, find and print the amount of money Monica will spend. If she doesn't have enough money to both a keyboard and a USB drive, print -1 instead. She will buy only the two required items.




For example - with a budget of 10, two keyboards costing 3,1 & finally three drives available costing 5,2,8, the answer should be 9 as she is only able to purchase the keyboard for 3 and a drive for 5.



More test cases are available on HackerRank.



I've attempted to solve this logically and with good performance in mind. I'm not sure if I should be happy with my solution. I would appreciate any feedback.



My solution (which works) or my GitHub repo -



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

namespace ElectronicsShop

class Program

static void Main(string[] args)


Console.WriteLine(GetMoneySpent(new int[] 3, 1 , new int[] 5, 2, 8 , 10));
Console.WriteLine(GetMoneySpent(new int[] 5, new int[] 4 , 5));
Console.ReadLine();


static int GetMoneySpent(int[] keyboards, int[] drives, int budget)

if (budget == 0)
return -1;

// sort the two arrays so the highest values are at the front
keyboards = SortArrayDescending(keyboards);
drives = SortArrayDescending(drives);

// delete any that are over our budget
var affordableKeyboards = GetAffordableItems(keyboards, budget);
var affordableDrives = GetAffordableItems(drives, budget);

// make a list to contain the combined totals
var combinedTotals = new List<int>();

foreach (var keyboard in keyboards)

foreach (var drive in drives)

combinedTotals.Add(keyboard + drive);



// sort the list & delete anything over budget
combinedTotals.Sort();
combinedTotals.Reverse();
combinedTotals.RemoveAll(n => n > budget);

return combinedTotals.Count == 0 ? -1 : combinedTotals[0];


static int[] SortArrayDescending(int[] array)

Array.Sort(array);
Array.Reverse(array);

return array;


static int[] GetAffordableItems(int[] array, int budget)

return array.Where(n => n < budget).ToArray();












share|improve this question











$endgroup$







  • 2




    $begingroup$
    Regarding the sample in the problem description, if Monica buys items costing $3$ and $5 ,$ wouldn't that just be $8 ?$ I think you meant that she buys the keyboard for $1$ and the USB-drive for $8 ,$ yielding the total of $9 .$
    $endgroup$
    – Nat
    15 hours ago











  • $begingroup$
    @Nat good spot! Yes you're right - I've fixed that now. Turns out I can't do basic Maths :)
    $endgroup$
    – Webbarr
    14 hours ago










  • $begingroup$
    Before going for good code you should go for a good algorithm. It seems your algorithm has $O(nk)$ runtime and memory. The problem can however be solved in $O(nlog n+klog k)$ time with $O(n+k)$ memory
    $endgroup$
    – Hagen von Eitzen
    3 hours ago











  • $begingroup$
    What you may and may not do after receiving answers
    $endgroup$
    – Jamal
    2 hours ago













7












7








7


2



$begingroup$


Challenge from Hacker Rank -




Monica wants to buy a keyboard and a USB drive from her favorite electronics store. The store has several models of each. Monica wants to spend as much as possible for the items, given her budget.



Given the price lists for the store's keyboards and USB drives, and Monica's budget, find and print the amount of money Monica will spend. If she doesn't have enough money to both a keyboard and a USB drive, print -1 instead. She will buy only the two required items.




For example - with a budget of 10, two keyboards costing 3,1 & finally three drives available costing 5,2,8, the answer should be 9 as she is only able to purchase the keyboard for 3 and a drive for 5.



More test cases are available on HackerRank.



I've attempted to solve this logically and with good performance in mind. I'm not sure if I should be happy with my solution. I would appreciate any feedback.



My solution (which works) or my GitHub repo -



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

namespace ElectronicsShop

class Program

static void Main(string[] args)


Console.WriteLine(GetMoneySpent(new int[] 3, 1 , new int[] 5, 2, 8 , 10));
Console.WriteLine(GetMoneySpent(new int[] 5, new int[] 4 , 5));
Console.ReadLine();


static int GetMoneySpent(int[] keyboards, int[] drives, int budget)

if (budget == 0)
return -1;

// sort the two arrays so the highest values are at the front
keyboards = SortArrayDescending(keyboards);
drives = SortArrayDescending(drives);

// delete any that are over our budget
var affordableKeyboards = GetAffordableItems(keyboards, budget);
var affordableDrives = GetAffordableItems(drives, budget);

// make a list to contain the combined totals
var combinedTotals = new List<int>();

foreach (var keyboard in keyboards)

foreach (var drive in drives)

combinedTotals.Add(keyboard + drive);



// sort the list & delete anything over budget
combinedTotals.Sort();
combinedTotals.Reverse();
combinedTotals.RemoveAll(n => n > budget);

return combinedTotals.Count == 0 ? -1 : combinedTotals[0];


static int[] SortArrayDescending(int[] array)

Array.Sort(array);
Array.Reverse(array);

return array;


static int[] GetAffordableItems(int[] array, int budget)

return array.Where(n => n < budget).ToArray();












share|improve this question











$endgroup$




Challenge from Hacker Rank -




Monica wants to buy a keyboard and a USB drive from her favorite electronics store. The store has several models of each. Monica wants to spend as much as possible for the items, given her budget.



Given the price lists for the store's keyboards and USB drives, and Monica's budget, find and print the amount of money Monica will spend. If she doesn't have enough money to both a keyboard and a USB drive, print -1 instead. She will buy only the two required items.




For example - with a budget of 10, two keyboards costing 3,1 & finally three drives available costing 5,2,8, the answer should be 9 as she is only able to purchase the keyboard for 3 and a drive for 5.



More test cases are available on HackerRank.



I've attempted to solve this logically and with good performance in mind. I'm not sure if I should be happy with my solution. I would appreciate any feedback.



My solution (which works) or my GitHub repo -



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

namespace ElectronicsShop

class Program

static void Main(string[] args)


Console.WriteLine(GetMoneySpent(new int[] 3, 1 , new int[] 5, 2, 8 , 10));
Console.WriteLine(GetMoneySpent(new int[] 5, new int[] 4 , 5));
Console.ReadLine();


static int GetMoneySpent(int[] keyboards, int[] drives, int budget)

if (budget == 0)
return -1;

// sort the two arrays so the highest values are at the front
keyboards = SortArrayDescending(keyboards);
drives = SortArrayDescending(drives);

// delete any that are over our budget
var affordableKeyboards = GetAffordableItems(keyboards, budget);
var affordableDrives = GetAffordableItems(drives, budget);

// make a list to contain the combined totals
var combinedTotals = new List<int>();

foreach (var keyboard in keyboards)

foreach (var drive in drives)

combinedTotals.Add(keyboard + drive);



// sort the list & delete anything over budget
combinedTotals.Sort();
combinedTotals.Reverse();
combinedTotals.RemoveAll(n => n > budget);

return combinedTotals.Count == 0 ? -1 : combinedTotals[0];


static int[] SortArrayDescending(int[] array)

Array.Sort(array);
Array.Reverse(array);

return array;


static int[] GetAffordableItems(int[] array, int budget)

return array.Where(n => n < budget).ToArray();









c# programming-challenge combinatorics






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 2 hours ago









Jamal

31.4k12 gold badges123 silver badges230 bronze badges




31.4k12 gold badges123 silver badges230 bronze badges










asked yesterday









WebbarrWebbarr

626 bronze badges




626 bronze badges







  • 2




    $begingroup$
    Regarding the sample in the problem description, if Monica buys items costing $3$ and $5 ,$ wouldn't that just be $8 ?$ I think you meant that she buys the keyboard for $1$ and the USB-drive for $8 ,$ yielding the total of $9 .$
    $endgroup$
    – Nat
    15 hours ago











  • $begingroup$
    @Nat good spot! Yes you're right - I've fixed that now. Turns out I can't do basic Maths :)
    $endgroup$
    – Webbarr
    14 hours ago










  • $begingroup$
    Before going for good code you should go for a good algorithm. It seems your algorithm has $O(nk)$ runtime and memory. The problem can however be solved in $O(nlog n+klog k)$ time with $O(n+k)$ memory
    $endgroup$
    – Hagen von Eitzen
    3 hours ago











  • $begingroup$
    What you may and may not do after receiving answers
    $endgroup$
    – Jamal
    2 hours ago












  • 2




    $begingroup$
    Regarding the sample in the problem description, if Monica buys items costing $3$ and $5 ,$ wouldn't that just be $8 ?$ I think you meant that she buys the keyboard for $1$ and the USB-drive for $8 ,$ yielding the total of $9 .$
    $endgroup$
    – Nat
    15 hours ago











  • $begingroup$
    @Nat good spot! Yes you're right - I've fixed that now. Turns out I can't do basic Maths :)
    $endgroup$
    – Webbarr
    14 hours ago










  • $begingroup$
    Before going for good code you should go for a good algorithm. It seems your algorithm has $O(nk)$ runtime and memory. The problem can however be solved in $O(nlog n+klog k)$ time with $O(n+k)$ memory
    $endgroup$
    – Hagen von Eitzen
    3 hours ago











  • $begingroup$
    What you may and may not do after receiving answers
    $endgroup$
    – Jamal
    2 hours ago







2




2




$begingroup$
Regarding the sample in the problem description, if Monica buys items costing $3$ and $5 ,$ wouldn't that just be $8 ?$ I think you meant that she buys the keyboard for $1$ and the USB-drive for $8 ,$ yielding the total of $9 .$
$endgroup$
– Nat
15 hours ago





$begingroup$
Regarding the sample in the problem description, if Monica buys items costing $3$ and $5 ,$ wouldn't that just be $8 ?$ I think you meant that she buys the keyboard for $1$ and the USB-drive for $8 ,$ yielding the total of $9 .$
$endgroup$
– Nat
15 hours ago













$begingroup$
@Nat good spot! Yes you're right - I've fixed that now. Turns out I can't do basic Maths :)
$endgroup$
– Webbarr
14 hours ago




$begingroup$
@Nat good spot! Yes you're right - I've fixed that now. Turns out I can't do basic Maths :)
$endgroup$
– Webbarr
14 hours ago












$begingroup$
Before going for good code you should go for a good algorithm. It seems your algorithm has $O(nk)$ runtime and memory. The problem can however be solved in $O(nlog n+klog k)$ time with $O(n+k)$ memory
$endgroup$
– Hagen von Eitzen
3 hours ago





$begingroup$
Before going for good code you should go for a good algorithm. It seems your algorithm has $O(nk)$ runtime and memory. The problem can however be solved in $O(nlog n+klog k)$ time with $O(n+k)$ memory
$endgroup$
– Hagen von Eitzen
3 hours ago













$begingroup$
What you may and may not do after receiving answers
$endgroup$
– Jamal
2 hours ago




$begingroup$
What you may and may not do after receiving answers
$endgroup$
– Jamal
2 hours ago










5 Answers
5






active

oldest

votes


















7












$begingroup$

early pruning



This is very nice:



 // delete any that are over our budget


Doing it before sorting can slightly speed the sorting operation.
I say slightly because "items over budget" is determined by the input,
and it will be some fraction f of an input item category,
so the savings is O(f * n * log n).



early discard



This is a bigger deal.



 // sort the list & delete anything over budget


For k keyboards and d drives, the sort does O(k * d * log k * d) work.
Discarding within this loop would be an even bigger win.



consistent idiom



It was a little odd that you used



 combinedTotals.RemoveAll(n => n > budget);


and



 array.Where(n => n < budget).ToArray();


to accomplish the same thing.
There's no speed difference but consider phrasing the same thing in the same way.



reversing



If you pass into Sort something that implements the IComparer interface,
you can change the comparison order and thus skip the Reverse step entirely.



arithmetic



Arbitrarily choose one of the categories as the driving category, perhaps keyboard.
Sort the drive prices, while leaving the keyboards in arbitrary order.
Note the min drive price, and use that along with budget for immediately pruning infeasible keyboards.



Loop over all surviving keyboards.
Target price is budget - kb_price.
Do binary search over drives for the target,
finding largest feasible drive,
and use that to update "best combo so far".
No need to sort them, you need only retain the "best" one.






share|improve this answer









$endgroup$








  • 1




    $begingroup$
    The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
    $endgroup$
    – Rick Davin
    8 hours ago


















5












$begingroup$

General Guidelines



  • You have coded everything in a single class Program. Take advantage of the fact C# is an object oriented language. Create at least one custom class that defines this problem.

  • Your current implementation is very strict and specific to 2 types of items. What if Monica needs to buy from n item types? It is up to you to decide the scope of your implementation, so what you have done is not wrong, but it is something to consider when building reusable code blocks. We can argue reusability of this exercise though.

  • When providing a method for consumers to use, make sure to include a handful of unit tests. This is an excellent way to get to know the outcome given any input. In this case, you are providing us GetMoneySpent, but we have to write our own unit tests to verify correctness of this method.

Review



  • You are using Array for a problem where IEnumerable could have also been used. Prefer the latter because you don't want to depend on fixed sized collections. You are converting ToArray(), this overhead should not be required when working with IEnumerable.





share|improve this answer











$endgroup$








  • 1




    $begingroup$
    In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
    $endgroup$
    – Webbarr
    yesterday







  • 1




    $begingroup$
    Well, it is tempting and easy to do it that way :)
    $endgroup$
    – dfhwze
    yesterday


















5












$begingroup$

The good thing first:



You divide and conquer the problem by creating some reasonable (and well named) methods. You could have gone all in by making methods for combining and final selection as well:



 ...
var combinedTotals = Combine(affordableKeyboards, affordableDrives);
return SelectMaxBuy(combinedTotals, budget);
}


But as shown below, dividing the code into such small methods can sometimes obscure more useful approaches.




It must be a mind slip that you find the affordable keyboards and drives, but you forget about them and iterate over the full arrays of keyboards and drives:




 // delete any that are over our budget
var affordableKeyboards = GetAffordableItems(keyboards, budget);
var affordableDrives = GetAffordableItems(drives, budget);

// make a list to contain the combined totals
var combinedTotals = new List<int>();

foreach (var keyboard in keyboards)

foreach (var drive in drives)

combinedTotals.Add(keyboard + drive);





I suppose that the loops should be:



 foreach (var keyboard in affordableKeyboards)

foreach (var drive in affordableDrives)

combinedTotals.Add(keyboard + drive);





Some optimizations:



 return array.Where(n => n < budget).ToArray();


Where has to iterate through the entire array, even if it is sorted.
A better approach would have been to sort ascending first, then take untill n > budget, and then reverse:



array.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse();


Making the almost same considerations with the combined totals:



 int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);


Your entire method could be refined to this:



static int GetMoneySpent(int[] keyboards, int[] drives, int budget)
budget <= 0)
return -1;

keyboards = keyboards.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();
drives = drives.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();

// make a list to contain the combined totals
var combinedTotals = new List<int>();

foreach (var keyboard in keyboards)

foreach (var drive in drives)

combinedTotals.Add(keyboard + drive);



int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);
return result == 0 ? -1 : result;




Just for the sport I made the below solution, that sorts the data sets in ascending order and iterate backwards to avoid reversing the data:



int GetMoneySpent(int[] keyboards, int[] drives, int budget)
budget <= 0)
return -1;

int result = -1;

Array.Sort(keyboards);
Array.Sort(drives);

int istart = keyboards.Length - 1;
while (istart >= 0 && keyboards[istart] > budget) istart--;
int jstart = drives.Length - 1;
while (jstart >= 0 && drives[jstart] > budget) jstart--;

for (int i = istart; i >= 0; i--)

int keyboard = keyboards[i];

for (int j = jstart; j >= 0; j--)

int drive = drives[j];

int price = keyboard + drive;
if (price < result)
break;

if (price > result && price <= budget)

result = price;




return result;






share|improve this answer











$endgroup$








  • 1




    $begingroup$
    Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
    $endgroup$
    – Webbarr
    yesterday







  • 1




    $begingroup$
    @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
    $endgroup$
    – Henrik Hansen
    18 hours ago


















5












$begingroup$

3 good answers already, but there's more!




I don't like that you modify the array you are given. This sort of thing would need to be documented, and generally creates confusion for all. You don't need arrays as inputs, so you could take IEnumerables instead without any added cost, which makes the code easier to reuse and communicates to the consumer that you aren't modifying anything. I'd consider making the parameter names a little more explicit:



public static int GetMoneySpent(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)


Your SortArrayDescending modifies the array given, and then proceeds to return it: this is how to really annoying people, because they will assume that because the method returns something that it won't be modifying the input.




You've clearly thought about edge cases, which is good. You might consider some parameter validation (e.g. checking the budget makes sense, the arrays should not be null):



if (budget < 0)
throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
if (keyboardPrices == null)
throw new ArgumentNullException(nameof(keyboardPrices));
if (drivePrices == null)
throw new ArgumentNullException(nameof(drivePrices));


At the moment the program would print -1, which is sort of makes sense, but could easily be the first clue that something has gone wrong higher-up.




As implied by J_H, you should discard before the sort. The following also clones the arrays immediately so we don't modify them:



// filter to within-budget items, sort the two arrays (ascending)
keyboards = keyboards.Where(k => k < budget).ToArray();
Array.Sort(keyboards);

drives = drives.Where(d => d < budget).ToArray();
Array.Sort(drives);



J_H has already described how you can get the optimal time complexity, but you can perform the loops at the end very simply, without needing nesting or binary search or any of that.



You also don't need to record a list of all the candidates, just keep track of the current best, as Henrik Hansen has already demonstrated:



// maximum within budget price
int max = -1;


If we start by looking at the most expensive keyboard and cheapest drive and simultaneously iterate through both, we can do this bit in linear time.



int ki = keyboards.Length - 1; // drive index
int di = 0; // drive index

while (ki >= 0 && di < drives.Length)

int candidate = keyboards[ki] + drives[di];
if (candidate <= budget)

max = Math.Max(candidate, max);
di++;

else

ki--;




Suppose we are looking at keyboard ki and drive di: candidate is the sum of their costs. If this candidate cost is no more than the budget, then it is a candidate for the max. We also know that we can check for a more pairing by looking at the next most expensive drive, di + 1. If instead the candidate was out of the budget, we know we can find a cheaper candidate by looking at the next cheapest keyboard ki - 1.



Basically, we look at each keyboard in turn, and cycle through the drives until we find the most expensive one we can get away with. When we find the first drive that is too expensive, we move onto the next keyboard. We know that we don't want any drive cheaper than the last one we looked at, because that could only produce a cheaper pair, so we can continue our search starting from the same drive.



At the end, we just return max: if we didn't find any candidates below budget, it will still be -1:



return max;



Concerning dfhwze's comment about buying more than 2 items: this process is essentially searching the Pareto front, which is done trivially and efficiently for 2 items, but becomes nightmarish for any more, so I would certainly forgive you for sticking to 2 lists ;)




The above code all in one, with added inline documentation to make the purpose explicit (useful for the consumer, so that they know exactly what it is meant to do, and useful for the maintainer, so that they also know what it is meant to do):



/// <summary>
/// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
/// Returns -1 if no pair is within budget.
/// </summary>
/// <param name="keyboardPrices">A list of prices of keyboards.</param>
/// <param name="drivepricess">A list of prices of drives.</param>
/// <param name="budget">The maximum budget. Must be non-negative</param>
public static int GetMoneySpent2(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

if (budget < 0)
throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
if (keyboardPrices == null)
throw new ArgumentNullException(nameof(keyboardPrices));
if (drivePrices == null)
throw new ArgumentNullException(nameof(drivePrices));

if (budget == 0)
return -1;

// filter to within-budget items, sort the two arrays (ascending)
var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
Array.Sort(keyboards);

var drives = drivePrices.Where(d => d < budget).ToArray();
Array.Sort(drives);

// maximum within budget price
int max = -1;

int ki = keyboards.Length - 1; // keyboard index
int di = 0; // drive index

while (ki >= 0 && di < drives.Length)

int candidate = keyboards[ki] + drives[di];
if (candidate <= budget)

max = Math.Max(candidate, max);
di++;

else

ki--;



return max;




J_H's solution (using a BinarySearch) could well be better in practise, because you only need to sort (and binary search) the shortest input: you can scan the other however you like. Implementation of that, since I too enjoy the sport:



/// <summary>
/// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
/// Returns -1 if no pair is within budget.
/// </summary>
/// <param name="keyboardPrices">A list of prices of keyboards.</param>
/// <param name="drivepricess">A list of prices of drives.</param>
/// <param name="budget">The maximum budget. Must be non-negative</param>
public static int GetMoneySpent3(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

if (budget < 0)
throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
if (keyboardPrices == null)
throw new ArgumentNullException(nameof(keyboardPrices));
if (drivePrices == null)
throw new ArgumentNullException(nameof(drivePrices));

if (budget == 0)
return -1;

// filter to within-budget items
var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
var drives = drivePrices.Where(d => d < budget).ToArray();

// determine which list is shorter
IReadOnlyList<int> longList;
int[] shortList;

if (keyboards.Length < drives.Length)

shortList = keyboards;
longList = drives;

else

shortList = drives;
longList = keyboards;


// special case of empty short-list
if (shortList.Length == 0)
return -1;

// sort shortList, to facilitate binary search
Array.Sort(shortList);

// maximum within budget price
int max = -1;

foreach (var k in longList)

// filter faster
if (k + shortList[0] > budget)
continue;

// find most expensive drive no more than budget - k
int i = Array.BinarySearch(shortList, budget - k);
i = i >= 0
? i // found
: ~i - 1; // not found, consider next smallest

// if such a drive exists, consider it a candidate
if (i >= 0)

int candidate = k + shortList[i];
max = Math.Max(max, candidate);



return max;






share|improve this answer











$endgroup$




















    1












    $begingroup$

    As others have pointed out, the code should be object oriented. It’s OK to start with procedural code as you have, but if you do, you should get in the habit of writing a quick test in your main entry point. Once you’ve written the first test, it can help you start seeing objects more clearly (and drive you to further unit tests.)



    For example: an ElectronicsShop would only have a catalog of items, but it has nothing to do with your spending habits. In your case, it would serve only as a data store for items.



    I’d really expect to see a Shopper class. The shopper would have both Money and a BuyingStrategy. The money could be a simple amount, but the strategy would be what you’re really working on. Finally, the strategy would expose a method Shop(Store, Items[], Budget).



    Inside Shop(), you’d retrieve the items from the store that meet your criteria: they’d be only keyboards and drives, and they’d be within budget. The shopper would add only eligible items to their comparison lists. Then comes time to evaluate them , so you’d add an Evaluate(Budget, ItemLists[]) method that would be called from within Shop(). Inside Evaluate() you can order results by price. But what happens when you get multiple answers that meet the same amount? A budget of 10 would be met by any of 9,1, 1,9, 6,4, or even 5,5! Which is more important, expensive drives or expensive keyboards? (In the real world, you’d go back to your product owner at this point and ask them about their priorities: “do you prefer the most expensive drive, the most expensive keyboard, or should it try to split the difference somehow?”) This might lead you to conclude that Evaluating is really the strategy, not Buying.



    I could go on, but I think I’ve made my point. Notice how once I’ve defined just a few relevant objects that the process I’m describing begins to mirror the real world act of shopping? And once you start down this path of viewing coding problems as models of real objects interacting in the real world, you’ll see the ease of defining objects and writing tests for them, and using them to recognize shortcomings in the specs and completing your specifications. (Pro tip: specifications are always incomplete.)



    Performance isn’t always the best starting point. Object oriented programming is less about finding the most mathematically efficient solution; it’s about building understandable components and proving that they meet your clients’ needs. Getting to the right answer is much more important than quickly getting to some answer that you can’t prove is correct. If performance is an issue after you’ve solved the problem, then start optimizing; but don’t start there.






    share|improve this answer











    $endgroup$















      Your Answer






      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "196"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224100%2fhackerrank-electronics-shop%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      7












      $begingroup$

      early pruning



      This is very nice:



       // delete any that are over our budget


      Doing it before sorting can slightly speed the sorting operation.
      I say slightly because "items over budget" is determined by the input,
      and it will be some fraction f of an input item category,
      so the savings is O(f * n * log n).



      early discard



      This is a bigger deal.



       // sort the list & delete anything over budget


      For k keyboards and d drives, the sort does O(k * d * log k * d) work.
      Discarding within this loop would be an even bigger win.



      consistent idiom



      It was a little odd that you used



       combinedTotals.RemoveAll(n => n > budget);


      and



       array.Where(n => n < budget).ToArray();


      to accomplish the same thing.
      There's no speed difference but consider phrasing the same thing in the same way.



      reversing



      If you pass into Sort something that implements the IComparer interface,
      you can change the comparison order and thus skip the Reverse step entirely.



      arithmetic



      Arbitrarily choose one of the categories as the driving category, perhaps keyboard.
      Sort the drive prices, while leaving the keyboards in arbitrary order.
      Note the min drive price, and use that along with budget for immediately pruning infeasible keyboards.



      Loop over all surviving keyboards.
      Target price is budget - kb_price.
      Do binary search over drives for the target,
      finding largest feasible drive,
      and use that to update "best combo so far".
      No need to sort them, you need only retain the "best" one.






      share|improve this answer









      $endgroup$








      • 1




        $begingroup$
        The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
        $endgroup$
        – Rick Davin
        8 hours ago















      7












      $begingroup$

      early pruning



      This is very nice:



       // delete any that are over our budget


      Doing it before sorting can slightly speed the sorting operation.
      I say slightly because "items over budget" is determined by the input,
      and it will be some fraction f of an input item category,
      so the savings is O(f * n * log n).



      early discard



      This is a bigger deal.



       // sort the list & delete anything over budget


      For k keyboards and d drives, the sort does O(k * d * log k * d) work.
      Discarding within this loop would be an even bigger win.



      consistent idiom



      It was a little odd that you used



       combinedTotals.RemoveAll(n => n > budget);


      and



       array.Where(n => n < budget).ToArray();


      to accomplish the same thing.
      There's no speed difference but consider phrasing the same thing in the same way.



      reversing



      If you pass into Sort something that implements the IComparer interface,
      you can change the comparison order and thus skip the Reverse step entirely.



      arithmetic



      Arbitrarily choose one of the categories as the driving category, perhaps keyboard.
      Sort the drive prices, while leaving the keyboards in arbitrary order.
      Note the min drive price, and use that along with budget for immediately pruning infeasible keyboards.



      Loop over all surviving keyboards.
      Target price is budget - kb_price.
      Do binary search over drives for the target,
      finding largest feasible drive,
      and use that to update "best combo so far".
      No need to sort them, you need only retain the "best" one.






      share|improve this answer









      $endgroup$








      • 1




        $begingroup$
        The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
        $endgroup$
        – Rick Davin
        8 hours ago













      7












      7








      7





      $begingroup$

      early pruning



      This is very nice:



       // delete any that are over our budget


      Doing it before sorting can slightly speed the sorting operation.
      I say slightly because "items over budget" is determined by the input,
      and it will be some fraction f of an input item category,
      so the savings is O(f * n * log n).



      early discard



      This is a bigger deal.



       // sort the list & delete anything over budget


      For k keyboards and d drives, the sort does O(k * d * log k * d) work.
      Discarding within this loop would be an even bigger win.



      consistent idiom



      It was a little odd that you used



       combinedTotals.RemoveAll(n => n > budget);


      and



       array.Where(n => n < budget).ToArray();


      to accomplish the same thing.
      There's no speed difference but consider phrasing the same thing in the same way.



      reversing



      If you pass into Sort something that implements the IComparer interface,
      you can change the comparison order and thus skip the Reverse step entirely.



      arithmetic



      Arbitrarily choose one of the categories as the driving category, perhaps keyboard.
      Sort the drive prices, while leaving the keyboards in arbitrary order.
      Note the min drive price, and use that along with budget for immediately pruning infeasible keyboards.



      Loop over all surviving keyboards.
      Target price is budget - kb_price.
      Do binary search over drives for the target,
      finding largest feasible drive,
      and use that to update "best combo so far".
      No need to sort them, you need only retain the "best" one.






      share|improve this answer









      $endgroup$



      early pruning



      This is very nice:



       // delete any that are over our budget


      Doing it before sorting can slightly speed the sorting operation.
      I say slightly because "items over budget" is determined by the input,
      and it will be some fraction f of an input item category,
      so the savings is O(f * n * log n).



      early discard



      This is a bigger deal.



       // sort the list & delete anything over budget


      For k keyboards and d drives, the sort does O(k * d * log k * d) work.
      Discarding within this loop would be an even bigger win.



      consistent idiom



      It was a little odd that you used



       combinedTotals.RemoveAll(n => n > budget);


      and



       array.Where(n => n < budget).ToArray();


      to accomplish the same thing.
      There's no speed difference but consider phrasing the same thing in the same way.



      reversing



      If you pass into Sort something that implements the IComparer interface,
      you can change the comparison order and thus skip the Reverse step entirely.



      arithmetic



      Arbitrarily choose one of the categories as the driving category, perhaps keyboard.
      Sort the drive prices, while leaving the keyboards in arbitrary order.
      Note the min drive price, and use that along with budget for immediately pruning infeasible keyboards.



      Loop over all surviving keyboards.
      Target price is budget - kb_price.
      Do binary search over drives for the target,
      finding largest feasible drive,
      and use that to update "best combo so far".
      No need to sort them, you need only retain the "best" one.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered yesterday









      J_HJ_H

      5,0921 silver badge38 bronze badges




      5,0921 silver badge38 bronze badges







      • 1




        $begingroup$
        The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
        $endgroup$
        – Rick Davin
        8 hours ago












      • 1




        $begingroup$
        The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
        $endgroup$
        – Rick Davin
        8 hours ago







      1




      1




      $begingroup$
      The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
      $endgroup$
      – Rick Davin
      8 hours ago




      $begingroup$
      The LINQ Where should be array.Where(n => n <= budget). But your key point is made.
      $endgroup$
      – Rick Davin
      8 hours ago













      5












      $begingroup$

      General Guidelines



      • You have coded everything in a single class Program. Take advantage of the fact C# is an object oriented language. Create at least one custom class that defines this problem.

      • Your current implementation is very strict and specific to 2 types of items. What if Monica needs to buy from n item types? It is up to you to decide the scope of your implementation, so what you have done is not wrong, but it is something to consider when building reusable code blocks. We can argue reusability of this exercise though.

      • When providing a method for consumers to use, make sure to include a handful of unit tests. This is an excellent way to get to know the outcome given any input. In this case, you are providing us GetMoneySpent, but we have to write our own unit tests to verify correctness of this method.

      Review



      • You are using Array for a problem where IEnumerable could have also been used. Prefer the latter because you don't want to depend on fixed sized collections. You are converting ToArray(), this overhead should not be required when working with IEnumerable.





      share|improve this answer











      $endgroup$








      • 1




        $begingroup$
        In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        Well, it is tempting and easy to do it that way :)
        $endgroup$
        – dfhwze
        yesterday















      5












      $begingroup$

      General Guidelines



      • You have coded everything in a single class Program. Take advantage of the fact C# is an object oriented language. Create at least one custom class that defines this problem.

      • Your current implementation is very strict and specific to 2 types of items. What if Monica needs to buy from n item types? It is up to you to decide the scope of your implementation, so what you have done is not wrong, but it is something to consider when building reusable code blocks. We can argue reusability of this exercise though.

      • When providing a method for consumers to use, make sure to include a handful of unit tests. This is an excellent way to get to know the outcome given any input. In this case, you are providing us GetMoneySpent, but we have to write our own unit tests to verify correctness of this method.

      Review



      • You are using Array for a problem where IEnumerable could have also been used. Prefer the latter because you don't want to depend on fixed sized collections. You are converting ToArray(), this overhead should not be required when working with IEnumerable.





      share|improve this answer











      $endgroup$








      • 1




        $begingroup$
        In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        Well, it is tempting and easy to do it that way :)
        $endgroup$
        – dfhwze
        yesterday













      5












      5








      5





      $begingroup$

      General Guidelines



      • You have coded everything in a single class Program. Take advantage of the fact C# is an object oriented language. Create at least one custom class that defines this problem.

      • Your current implementation is very strict and specific to 2 types of items. What if Monica needs to buy from n item types? It is up to you to decide the scope of your implementation, so what you have done is not wrong, but it is something to consider when building reusable code blocks. We can argue reusability of this exercise though.

      • When providing a method for consumers to use, make sure to include a handful of unit tests. This is an excellent way to get to know the outcome given any input. In this case, you are providing us GetMoneySpent, but we have to write our own unit tests to verify correctness of this method.

      Review



      • You are using Array for a problem where IEnumerable could have also been used. Prefer the latter because you don't want to depend on fixed sized collections. You are converting ToArray(), this overhead should not be required when working with IEnumerable.





      share|improve this answer











      $endgroup$



      General Guidelines



      • You have coded everything in a single class Program. Take advantage of the fact C# is an object oriented language. Create at least one custom class that defines this problem.

      • Your current implementation is very strict and specific to 2 types of items. What if Monica needs to buy from n item types? It is up to you to decide the scope of your implementation, so what you have done is not wrong, but it is something to consider when building reusable code blocks. We can argue reusability of this exercise though.

      • When providing a method for consumers to use, make sure to include a handful of unit tests. This is an excellent way to get to know the outcome given any input. In this case, you are providing us GetMoneySpent, but we have to write our own unit tests to verify correctness of this method.

      Review



      • You are using Array for a problem where IEnumerable could have also been used. Prefer the latter because you don't want to depend on fixed sized collections. You are converting ToArray(), this overhead should not be required when working with IEnumerable.






      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited yesterday

























      answered yesterday









      dfhwzedfhwze

      4,6511 gold badge7 silver badges34 bronze badges




      4,6511 gold badge7 silver badges34 bronze badges







      • 1




        $begingroup$
        In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        Well, it is tempting and easy to do it that way :)
        $endgroup$
        – dfhwze
        yesterday












      • 1




        $begingroup$
        In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        Well, it is tempting and easy to do it that way :)
        $endgroup$
        – dfhwze
        yesterday







      1




      1




      $begingroup$
      In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
      $endgroup$
      – Webbarr
      yesterday





      $begingroup$
      In terms of your first point, you're 100% correct. I tend to fall into a bad habbit of writing procedural code when I'm doing these types of challenges. I really shouldn't, kind of defeats the purpose. I get what you mean for point 2, I guess I kinda limited it because it was for a one time use in this specific challenge. It's a good idea to look at reusability for exercises though. Sorry about the lack of unit tests - I need to spend some more time improving my understanding of how they work, especially if I'm going to be posting stuff on code review.
      $endgroup$
      – Webbarr
      yesterday





      1




      1




      $begingroup$
      Well, it is tempting and easy to do it that way :)
      $endgroup$
      – dfhwze
      yesterday




      $begingroup$
      Well, it is tempting and easy to do it that way :)
      $endgroup$
      – dfhwze
      yesterday











      5












      $begingroup$

      The good thing first:



      You divide and conquer the problem by creating some reasonable (and well named) methods. You could have gone all in by making methods for combining and final selection as well:



       ...
      var combinedTotals = Combine(affordableKeyboards, affordableDrives);
      return SelectMaxBuy(combinedTotals, budget);
      }


      But as shown below, dividing the code into such small methods can sometimes obscure more useful approaches.




      It must be a mind slip that you find the affordable keyboards and drives, but you forget about them and iterate over the full arrays of keyboards and drives:




       // delete any that are over our budget
      var affordableKeyboards = GetAffordableItems(keyboards, budget);
      var affordableDrives = GetAffordableItems(drives, budget);

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);





      I suppose that the loops should be:



       foreach (var keyboard in affordableKeyboards)

      foreach (var drive in affordableDrives)

      combinedTotals.Add(keyboard + drive);





      Some optimizations:



       return array.Where(n => n < budget).ToArray();


      Where has to iterate through the entire array, even if it is sorted.
      A better approach would have been to sort ascending first, then take untill n > budget, and then reverse:



      array.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse();


      Making the almost same considerations with the combined totals:



       int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);


      Your entire method could be refined to this:



      static int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      keyboards = keyboards.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();
      drives = drives.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);



      int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);
      return result == 0 ? -1 : result;




      Just for the sport I made the below solution, that sorts the data sets in ascending order and iterate backwards to avoid reversing the data:



      int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      int result = -1;

      Array.Sort(keyboards);
      Array.Sort(drives);

      int istart = keyboards.Length - 1;
      while (istart >= 0 && keyboards[istart] > budget) istart--;
      int jstart = drives.Length - 1;
      while (jstart >= 0 && drives[jstart] > budget) jstart--;

      for (int i = istart; i >= 0; i--)

      int keyboard = keyboards[i];

      for (int j = jstart; j >= 0; j--)

      int drive = drives[j];

      int price = keyboard + drive;
      if (price < result)
      break;

      if (price > result && price <= budget)

      result = price;




      return result;






      share|improve this answer











      $endgroup$








      • 1




        $begingroup$
        Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
        $endgroup$
        – Henrik Hansen
        18 hours ago















      5












      $begingroup$

      The good thing first:



      You divide and conquer the problem by creating some reasonable (and well named) methods. You could have gone all in by making methods for combining and final selection as well:



       ...
      var combinedTotals = Combine(affordableKeyboards, affordableDrives);
      return SelectMaxBuy(combinedTotals, budget);
      }


      But as shown below, dividing the code into such small methods can sometimes obscure more useful approaches.




      It must be a mind slip that you find the affordable keyboards and drives, but you forget about them and iterate over the full arrays of keyboards and drives:




       // delete any that are over our budget
      var affordableKeyboards = GetAffordableItems(keyboards, budget);
      var affordableDrives = GetAffordableItems(drives, budget);

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);





      I suppose that the loops should be:



       foreach (var keyboard in affordableKeyboards)

      foreach (var drive in affordableDrives)

      combinedTotals.Add(keyboard + drive);





      Some optimizations:



       return array.Where(n => n < budget).ToArray();


      Where has to iterate through the entire array, even if it is sorted.
      A better approach would have been to sort ascending first, then take untill n > budget, and then reverse:



      array.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse();


      Making the almost same considerations with the combined totals:



       int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);


      Your entire method could be refined to this:



      static int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      keyboards = keyboards.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();
      drives = drives.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);



      int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);
      return result == 0 ? -1 : result;




      Just for the sport I made the below solution, that sorts the data sets in ascending order and iterate backwards to avoid reversing the data:



      int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      int result = -1;

      Array.Sort(keyboards);
      Array.Sort(drives);

      int istart = keyboards.Length - 1;
      while (istart >= 0 && keyboards[istart] > budget) istart--;
      int jstart = drives.Length - 1;
      while (jstart >= 0 && drives[jstart] > budget) jstart--;

      for (int i = istart; i >= 0; i--)

      int keyboard = keyboards[i];

      for (int j = jstart; j >= 0; j--)

      int drive = drives[j];

      int price = keyboard + drive;
      if (price < result)
      break;

      if (price > result && price <= budget)

      result = price;




      return result;






      share|improve this answer











      $endgroup$








      • 1




        $begingroup$
        Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
        $endgroup$
        – Henrik Hansen
        18 hours ago













      5












      5








      5





      $begingroup$

      The good thing first:



      You divide and conquer the problem by creating some reasonable (and well named) methods. You could have gone all in by making methods for combining and final selection as well:



       ...
      var combinedTotals = Combine(affordableKeyboards, affordableDrives);
      return SelectMaxBuy(combinedTotals, budget);
      }


      But as shown below, dividing the code into such small methods can sometimes obscure more useful approaches.




      It must be a mind slip that you find the affordable keyboards and drives, but you forget about them and iterate over the full arrays of keyboards and drives:




       // delete any that are over our budget
      var affordableKeyboards = GetAffordableItems(keyboards, budget);
      var affordableDrives = GetAffordableItems(drives, budget);

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);





      I suppose that the loops should be:



       foreach (var keyboard in affordableKeyboards)

      foreach (var drive in affordableDrives)

      combinedTotals.Add(keyboard + drive);





      Some optimizations:



       return array.Where(n => n < budget).ToArray();


      Where has to iterate through the entire array, even if it is sorted.
      A better approach would have been to sort ascending first, then take untill n > budget, and then reverse:



      array.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse();


      Making the almost same considerations with the combined totals:



       int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);


      Your entire method could be refined to this:



      static int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      keyboards = keyboards.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();
      drives = drives.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);



      int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);
      return result == 0 ? -1 : result;




      Just for the sport I made the below solution, that sorts the data sets in ascending order and iterate backwards to avoid reversing the data:



      int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      int result = -1;

      Array.Sort(keyboards);
      Array.Sort(drives);

      int istart = keyboards.Length - 1;
      while (istart >= 0 && keyboards[istart] > budget) istart--;
      int jstart = drives.Length - 1;
      while (jstart >= 0 && drives[jstart] > budget) jstart--;

      for (int i = istart; i >= 0; i--)

      int keyboard = keyboards[i];

      for (int j = jstart; j >= 0; j--)

      int drive = drives[j];

      int price = keyboard + drive;
      if (price < result)
      break;

      if (price > result && price <= budget)

      result = price;




      return result;






      share|improve this answer











      $endgroup$



      The good thing first:



      You divide and conquer the problem by creating some reasonable (and well named) methods. You could have gone all in by making methods for combining and final selection as well:



       ...
      var combinedTotals = Combine(affordableKeyboards, affordableDrives);
      return SelectMaxBuy(combinedTotals, budget);
      }


      But as shown below, dividing the code into such small methods can sometimes obscure more useful approaches.




      It must be a mind slip that you find the affordable keyboards and drives, but you forget about them and iterate over the full arrays of keyboards and drives:




       // delete any that are over our budget
      var affordableKeyboards = GetAffordableItems(keyboards, budget);
      var affordableDrives = GetAffordableItems(drives, budget);

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);





      I suppose that the loops should be:



       foreach (var keyboard in affordableKeyboards)

      foreach (var drive in affordableDrives)

      combinedTotals.Add(keyboard + drive);





      Some optimizations:



       return array.Where(n => n < budget).ToArray();


      Where has to iterate through the entire array, even if it is sorted.
      A better approach would have been to sort ascending first, then take untill n > budget, and then reverse:



      array.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse();


      Making the almost same considerations with the combined totals:



       int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);


      Your entire method could be refined to this:



      static int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      keyboards = keyboards.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();
      drives = drives.OrderBy(n => n).TakeWhile(n => n <= budget).Reverse().ToArray();

      // make a list to contain the combined totals
      var combinedTotals = new List<int>();

      foreach (var keyboard in keyboards)

      foreach (var drive in drives)

      combinedTotals.Add(keyboard + drive);



      int result = combinedTotals.OrderByDescending(n => n).FirstOrDefault(n => n <= budget);
      return result == 0 ? -1 : result;




      Just for the sport I made the below solution, that sorts the data sets in ascending order and iterate backwards to avoid reversing the data:



      int GetMoneySpent(int[] keyboards, int[] drives, int budget)
      budget <= 0)
      return -1;

      int result = -1;

      Array.Sort(keyboards);
      Array.Sort(drives);

      int istart = keyboards.Length - 1;
      while (istart >= 0 && keyboards[istart] > budget) istart--;
      int jstart = drives.Length - 1;
      while (jstart >= 0 && drives[jstart] > budget) jstart--;

      for (int i = istart; i >= 0; i--)

      int keyboard = keyboards[i];

      for (int j = jstart; j >= 0; j--)

      int drive = drives[j];

      int price = keyboard + drive;
      if (price < result)
      break;

      if (price > result && price <= budget)

      result = price;




      return result;







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited yesterday









      Webbarr

      626 bronze badges




      626 bronze badges










      answered yesterday









      Henrik HansenHenrik Hansen

      10.6k1 gold badge13 silver badges38 bronze badges




      10.6k1 gold badge13 silver badges38 bronze badges







      • 1




        $begingroup$
        Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
        $endgroup$
        – Henrik Hansen
        18 hours ago












      • 1




        $begingroup$
        Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
        $endgroup$
        – Webbarr
        yesterday







      • 1




        $begingroup$
        @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
        $endgroup$
        – Henrik Hansen
        18 hours ago







      1




      1




      $begingroup$
      Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
      $endgroup$
      – Webbarr
      yesterday





      $begingroup$
      Well that's embarassing. Definitely a slip of the brain in terms of forgetting to loop through the affordableKeyboards & affordableDrives arrays! Appreciate the feedback though, definite +1 from me.
      $endgroup$
      – Webbarr
      yesterday





      1




      1




      $begingroup$
      @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
      $endgroup$
      – Henrik Hansen
      18 hours ago




      $begingroup$
      @Webbarr: I think, most of us know the feeling. You're not the first and won't be the last :-)
      $endgroup$
      – Henrik Hansen
      18 hours ago











      5












      $begingroup$

      3 good answers already, but there's more!




      I don't like that you modify the array you are given. This sort of thing would need to be documented, and generally creates confusion for all. You don't need arrays as inputs, so you could take IEnumerables instead without any added cost, which makes the code easier to reuse and communicates to the consumer that you aren't modifying anything. I'd consider making the parameter names a little more explicit:



      public static int GetMoneySpent(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)


      Your SortArrayDescending modifies the array given, and then proceeds to return it: this is how to really annoying people, because they will assume that because the method returns something that it won't be modifying the input.




      You've clearly thought about edge cases, which is good. You might consider some parameter validation (e.g. checking the budget makes sense, the arrays should not be null):



      if (budget < 0)
      throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
      if (keyboardPrices == null)
      throw new ArgumentNullException(nameof(keyboardPrices));
      if (drivePrices == null)
      throw new ArgumentNullException(nameof(drivePrices));


      At the moment the program would print -1, which is sort of makes sense, but could easily be the first clue that something has gone wrong higher-up.




      As implied by J_H, you should discard before the sort. The following also clones the arrays immediately so we don't modify them:



      // filter to within-budget items, sort the two arrays (ascending)
      keyboards = keyboards.Where(k => k < budget).ToArray();
      Array.Sort(keyboards);

      drives = drives.Where(d => d < budget).ToArray();
      Array.Sort(drives);



      J_H has already described how you can get the optimal time complexity, but you can perform the loops at the end very simply, without needing nesting or binary search or any of that.



      You also don't need to record a list of all the candidates, just keep track of the current best, as Henrik Hansen has already demonstrated:



      // maximum within budget price
      int max = -1;


      If we start by looking at the most expensive keyboard and cheapest drive and simultaneously iterate through both, we can do this bit in linear time.



      int ki = keyboards.Length - 1; // drive index
      int di = 0; // drive index

      while (ki >= 0 && di < drives.Length)

      int candidate = keyboards[ki] + drives[di];
      if (candidate <= budget)

      max = Math.Max(candidate, max);
      di++;

      else

      ki--;




      Suppose we are looking at keyboard ki and drive di: candidate is the sum of their costs. If this candidate cost is no more than the budget, then it is a candidate for the max. We also know that we can check for a more pairing by looking at the next most expensive drive, di + 1. If instead the candidate was out of the budget, we know we can find a cheaper candidate by looking at the next cheapest keyboard ki - 1.



      Basically, we look at each keyboard in turn, and cycle through the drives until we find the most expensive one we can get away with. When we find the first drive that is too expensive, we move onto the next keyboard. We know that we don't want any drive cheaper than the last one we looked at, because that could only produce a cheaper pair, so we can continue our search starting from the same drive.



      At the end, we just return max: if we didn't find any candidates below budget, it will still be -1:



      return max;



      Concerning dfhwze's comment about buying more than 2 items: this process is essentially searching the Pareto front, which is done trivially and efficiently for 2 items, but becomes nightmarish for any more, so I would certainly forgive you for sticking to 2 lists ;)




      The above code all in one, with added inline documentation to make the purpose explicit (useful for the consumer, so that they know exactly what it is meant to do, and useful for the maintainer, so that they also know what it is meant to do):



      /// <summary>
      /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
      /// Returns -1 if no pair is within budget.
      /// </summary>
      /// <param name="keyboardPrices">A list of prices of keyboards.</param>
      /// <param name="drivepricess">A list of prices of drives.</param>
      /// <param name="budget">The maximum budget. Must be non-negative</param>
      public static int GetMoneySpent2(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

      if (budget < 0)
      throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
      if (keyboardPrices == null)
      throw new ArgumentNullException(nameof(keyboardPrices));
      if (drivePrices == null)
      throw new ArgumentNullException(nameof(drivePrices));

      if (budget == 0)
      return -1;

      // filter to within-budget items, sort the two arrays (ascending)
      var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
      Array.Sort(keyboards);

      var drives = drivePrices.Where(d => d < budget).ToArray();
      Array.Sort(drives);

      // maximum within budget price
      int max = -1;

      int ki = keyboards.Length - 1; // keyboard index
      int di = 0; // drive index

      while (ki >= 0 && di < drives.Length)

      int candidate = keyboards[ki] + drives[di];
      if (candidate <= budget)

      max = Math.Max(candidate, max);
      di++;

      else

      ki--;



      return max;




      J_H's solution (using a BinarySearch) could well be better in practise, because you only need to sort (and binary search) the shortest input: you can scan the other however you like. Implementation of that, since I too enjoy the sport:



      /// <summary>
      /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
      /// Returns -1 if no pair is within budget.
      /// </summary>
      /// <param name="keyboardPrices">A list of prices of keyboards.</param>
      /// <param name="drivepricess">A list of prices of drives.</param>
      /// <param name="budget">The maximum budget. Must be non-negative</param>
      public static int GetMoneySpent3(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

      if (budget < 0)
      throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
      if (keyboardPrices == null)
      throw new ArgumentNullException(nameof(keyboardPrices));
      if (drivePrices == null)
      throw new ArgumentNullException(nameof(drivePrices));

      if (budget == 0)
      return -1;

      // filter to within-budget items
      var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
      var drives = drivePrices.Where(d => d < budget).ToArray();

      // determine which list is shorter
      IReadOnlyList<int> longList;
      int[] shortList;

      if (keyboards.Length < drives.Length)

      shortList = keyboards;
      longList = drives;

      else

      shortList = drives;
      longList = keyboards;


      // special case of empty short-list
      if (shortList.Length == 0)
      return -1;

      // sort shortList, to facilitate binary search
      Array.Sort(shortList);

      // maximum within budget price
      int max = -1;

      foreach (var k in longList)

      // filter faster
      if (k + shortList[0] > budget)
      continue;

      // find most expensive drive no more than budget - k
      int i = Array.BinarySearch(shortList, budget - k);
      i = i >= 0
      ? i // found
      : ~i - 1; // not found, consider next smallest

      // if such a drive exists, consider it a candidate
      if (i >= 0)

      int candidate = k + shortList[i];
      max = Math.Max(max, candidate);



      return max;






      share|improve this answer











      $endgroup$

















        5












        $begingroup$

        3 good answers already, but there's more!




        I don't like that you modify the array you are given. This sort of thing would need to be documented, and generally creates confusion for all. You don't need arrays as inputs, so you could take IEnumerables instead without any added cost, which makes the code easier to reuse and communicates to the consumer that you aren't modifying anything. I'd consider making the parameter names a little more explicit:



        public static int GetMoneySpent(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)


        Your SortArrayDescending modifies the array given, and then proceeds to return it: this is how to really annoying people, because they will assume that because the method returns something that it won't be modifying the input.




        You've clearly thought about edge cases, which is good. You might consider some parameter validation (e.g. checking the budget makes sense, the arrays should not be null):



        if (budget < 0)
        throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
        if (keyboardPrices == null)
        throw new ArgumentNullException(nameof(keyboardPrices));
        if (drivePrices == null)
        throw new ArgumentNullException(nameof(drivePrices));


        At the moment the program would print -1, which is sort of makes sense, but could easily be the first clue that something has gone wrong higher-up.




        As implied by J_H, you should discard before the sort. The following also clones the arrays immediately so we don't modify them:



        // filter to within-budget items, sort the two arrays (ascending)
        keyboards = keyboards.Where(k => k < budget).ToArray();
        Array.Sort(keyboards);

        drives = drives.Where(d => d < budget).ToArray();
        Array.Sort(drives);



        J_H has already described how you can get the optimal time complexity, but you can perform the loops at the end very simply, without needing nesting or binary search or any of that.



        You also don't need to record a list of all the candidates, just keep track of the current best, as Henrik Hansen has already demonstrated:



        // maximum within budget price
        int max = -1;


        If we start by looking at the most expensive keyboard and cheapest drive and simultaneously iterate through both, we can do this bit in linear time.



        int ki = keyboards.Length - 1; // drive index
        int di = 0; // drive index

        while (ki >= 0 && di < drives.Length)

        int candidate = keyboards[ki] + drives[di];
        if (candidate <= budget)

        max = Math.Max(candidate, max);
        di++;

        else

        ki--;




        Suppose we are looking at keyboard ki and drive di: candidate is the sum of their costs. If this candidate cost is no more than the budget, then it is a candidate for the max. We also know that we can check for a more pairing by looking at the next most expensive drive, di + 1. If instead the candidate was out of the budget, we know we can find a cheaper candidate by looking at the next cheapest keyboard ki - 1.



        Basically, we look at each keyboard in turn, and cycle through the drives until we find the most expensive one we can get away with. When we find the first drive that is too expensive, we move onto the next keyboard. We know that we don't want any drive cheaper than the last one we looked at, because that could only produce a cheaper pair, so we can continue our search starting from the same drive.



        At the end, we just return max: if we didn't find any candidates below budget, it will still be -1:



        return max;



        Concerning dfhwze's comment about buying more than 2 items: this process is essentially searching the Pareto front, which is done trivially and efficiently for 2 items, but becomes nightmarish for any more, so I would certainly forgive you for sticking to 2 lists ;)




        The above code all in one, with added inline documentation to make the purpose explicit (useful for the consumer, so that they know exactly what it is meant to do, and useful for the maintainer, so that they also know what it is meant to do):



        /// <summary>
        /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
        /// Returns -1 if no pair is within budget.
        /// </summary>
        /// <param name="keyboardPrices">A list of prices of keyboards.</param>
        /// <param name="drivepricess">A list of prices of drives.</param>
        /// <param name="budget">The maximum budget. Must be non-negative</param>
        public static int GetMoneySpent2(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

        if (budget < 0)
        throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
        if (keyboardPrices == null)
        throw new ArgumentNullException(nameof(keyboardPrices));
        if (drivePrices == null)
        throw new ArgumentNullException(nameof(drivePrices));

        if (budget == 0)
        return -1;

        // filter to within-budget items, sort the two arrays (ascending)
        var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
        Array.Sort(keyboards);

        var drives = drivePrices.Where(d => d < budget).ToArray();
        Array.Sort(drives);

        // maximum within budget price
        int max = -1;

        int ki = keyboards.Length - 1; // keyboard index
        int di = 0; // drive index

        while (ki >= 0 && di < drives.Length)

        int candidate = keyboards[ki] + drives[di];
        if (candidate <= budget)

        max = Math.Max(candidate, max);
        di++;

        else

        ki--;



        return max;




        J_H's solution (using a BinarySearch) could well be better in practise, because you only need to sort (and binary search) the shortest input: you can scan the other however you like. Implementation of that, since I too enjoy the sport:



        /// <summary>
        /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
        /// Returns -1 if no pair is within budget.
        /// </summary>
        /// <param name="keyboardPrices">A list of prices of keyboards.</param>
        /// <param name="drivepricess">A list of prices of drives.</param>
        /// <param name="budget">The maximum budget. Must be non-negative</param>
        public static int GetMoneySpent3(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

        if (budget < 0)
        throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
        if (keyboardPrices == null)
        throw new ArgumentNullException(nameof(keyboardPrices));
        if (drivePrices == null)
        throw new ArgumentNullException(nameof(drivePrices));

        if (budget == 0)
        return -1;

        // filter to within-budget items
        var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
        var drives = drivePrices.Where(d => d < budget).ToArray();

        // determine which list is shorter
        IReadOnlyList<int> longList;
        int[] shortList;

        if (keyboards.Length < drives.Length)

        shortList = keyboards;
        longList = drives;

        else

        shortList = drives;
        longList = keyboards;


        // special case of empty short-list
        if (shortList.Length == 0)
        return -1;

        // sort shortList, to facilitate binary search
        Array.Sort(shortList);

        // maximum within budget price
        int max = -1;

        foreach (var k in longList)

        // filter faster
        if (k + shortList[0] > budget)
        continue;

        // find most expensive drive no more than budget - k
        int i = Array.BinarySearch(shortList, budget - k);
        i = i >= 0
        ? i // found
        : ~i - 1; // not found, consider next smallest

        // if such a drive exists, consider it a candidate
        if (i >= 0)

        int candidate = k + shortList[i];
        max = Math.Max(max, candidate);



        return max;






        share|improve this answer











        $endgroup$















          5












          5








          5





          $begingroup$

          3 good answers already, but there's more!




          I don't like that you modify the array you are given. This sort of thing would need to be documented, and generally creates confusion for all. You don't need arrays as inputs, so you could take IEnumerables instead without any added cost, which makes the code easier to reuse and communicates to the consumer that you aren't modifying anything. I'd consider making the parameter names a little more explicit:



          public static int GetMoneySpent(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)


          Your SortArrayDescending modifies the array given, and then proceeds to return it: this is how to really annoying people, because they will assume that because the method returns something that it won't be modifying the input.




          You've clearly thought about edge cases, which is good. You might consider some parameter validation (e.g. checking the budget makes sense, the arrays should not be null):



          if (budget < 0)
          throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
          if (keyboardPrices == null)
          throw new ArgumentNullException(nameof(keyboardPrices));
          if (drivePrices == null)
          throw new ArgumentNullException(nameof(drivePrices));


          At the moment the program would print -1, which is sort of makes sense, but could easily be the first clue that something has gone wrong higher-up.




          As implied by J_H, you should discard before the sort. The following also clones the arrays immediately so we don't modify them:



          // filter to within-budget items, sort the two arrays (ascending)
          keyboards = keyboards.Where(k => k < budget).ToArray();
          Array.Sort(keyboards);

          drives = drives.Where(d => d < budget).ToArray();
          Array.Sort(drives);



          J_H has already described how you can get the optimal time complexity, but you can perform the loops at the end very simply, without needing nesting or binary search or any of that.



          You also don't need to record a list of all the candidates, just keep track of the current best, as Henrik Hansen has already demonstrated:



          // maximum within budget price
          int max = -1;


          If we start by looking at the most expensive keyboard and cheapest drive and simultaneously iterate through both, we can do this bit in linear time.



          int ki = keyboards.Length - 1; // drive index
          int di = 0; // drive index

          while (ki >= 0 && di < drives.Length)

          int candidate = keyboards[ki] + drives[di];
          if (candidate <= budget)

          max = Math.Max(candidate, max);
          di++;

          else

          ki--;




          Suppose we are looking at keyboard ki and drive di: candidate is the sum of their costs. If this candidate cost is no more than the budget, then it is a candidate for the max. We also know that we can check for a more pairing by looking at the next most expensive drive, di + 1. If instead the candidate was out of the budget, we know we can find a cheaper candidate by looking at the next cheapest keyboard ki - 1.



          Basically, we look at each keyboard in turn, and cycle through the drives until we find the most expensive one we can get away with. When we find the first drive that is too expensive, we move onto the next keyboard. We know that we don't want any drive cheaper than the last one we looked at, because that could only produce a cheaper pair, so we can continue our search starting from the same drive.



          At the end, we just return max: if we didn't find any candidates below budget, it will still be -1:



          return max;



          Concerning dfhwze's comment about buying more than 2 items: this process is essentially searching the Pareto front, which is done trivially and efficiently for 2 items, but becomes nightmarish for any more, so I would certainly forgive you for sticking to 2 lists ;)




          The above code all in one, with added inline documentation to make the purpose explicit (useful for the consumer, so that they know exactly what it is meant to do, and useful for the maintainer, so that they also know what it is meant to do):



          /// <summary>
          /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
          /// Returns -1 if no pair is within budget.
          /// </summary>
          /// <param name="keyboardPrices">A list of prices of keyboards.</param>
          /// <param name="drivepricess">A list of prices of drives.</param>
          /// <param name="budget">The maximum budget. Must be non-negative</param>
          public static int GetMoneySpent2(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

          if (budget < 0)
          throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
          if (keyboardPrices == null)
          throw new ArgumentNullException(nameof(keyboardPrices));
          if (drivePrices == null)
          throw new ArgumentNullException(nameof(drivePrices));

          if (budget == 0)
          return -1;

          // filter to within-budget items, sort the two arrays (ascending)
          var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
          Array.Sort(keyboards);

          var drives = drivePrices.Where(d => d < budget).ToArray();
          Array.Sort(drives);

          // maximum within budget price
          int max = -1;

          int ki = keyboards.Length - 1; // keyboard index
          int di = 0; // drive index

          while (ki >= 0 && di < drives.Length)

          int candidate = keyboards[ki] + drives[di];
          if (candidate <= budget)

          max = Math.Max(candidate, max);
          di++;

          else

          ki--;



          return max;




          J_H's solution (using a BinarySearch) could well be better in practise, because you only need to sort (and binary search) the shortest input: you can scan the other however you like. Implementation of that, since I too enjoy the sport:



          /// <summary>
          /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
          /// Returns -1 if no pair is within budget.
          /// </summary>
          /// <param name="keyboardPrices">A list of prices of keyboards.</param>
          /// <param name="drivepricess">A list of prices of drives.</param>
          /// <param name="budget">The maximum budget. Must be non-negative</param>
          public static int GetMoneySpent3(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

          if (budget < 0)
          throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
          if (keyboardPrices == null)
          throw new ArgumentNullException(nameof(keyboardPrices));
          if (drivePrices == null)
          throw new ArgumentNullException(nameof(drivePrices));

          if (budget == 0)
          return -1;

          // filter to within-budget items
          var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
          var drives = drivePrices.Where(d => d < budget).ToArray();

          // determine which list is shorter
          IReadOnlyList<int> longList;
          int[] shortList;

          if (keyboards.Length < drives.Length)

          shortList = keyboards;
          longList = drives;

          else

          shortList = drives;
          longList = keyboards;


          // special case of empty short-list
          if (shortList.Length == 0)
          return -1;

          // sort shortList, to facilitate binary search
          Array.Sort(shortList);

          // maximum within budget price
          int max = -1;

          foreach (var k in longList)

          // filter faster
          if (k + shortList[0] > budget)
          continue;

          // find most expensive drive no more than budget - k
          int i = Array.BinarySearch(shortList, budget - k);
          i = i >= 0
          ? i // found
          : ~i - 1; // not found, consider next smallest

          // if such a drive exists, consider it a candidate
          if (i >= 0)

          int candidate = k + shortList[i];
          max = Math.Max(max, candidate);



          return max;






          share|improve this answer











          $endgroup$



          3 good answers already, but there's more!




          I don't like that you modify the array you are given. This sort of thing would need to be documented, and generally creates confusion for all. You don't need arrays as inputs, so you could take IEnumerables instead without any added cost, which makes the code easier to reuse and communicates to the consumer that you aren't modifying anything. I'd consider making the parameter names a little more explicit:



          public static int GetMoneySpent(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)


          Your SortArrayDescending modifies the array given, and then proceeds to return it: this is how to really annoying people, because they will assume that because the method returns something that it won't be modifying the input.




          You've clearly thought about edge cases, which is good. You might consider some parameter validation (e.g. checking the budget makes sense, the arrays should not be null):



          if (budget < 0)
          throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
          if (keyboardPrices == null)
          throw new ArgumentNullException(nameof(keyboardPrices));
          if (drivePrices == null)
          throw new ArgumentNullException(nameof(drivePrices));


          At the moment the program would print -1, which is sort of makes sense, but could easily be the first clue that something has gone wrong higher-up.




          As implied by J_H, you should discard before the sort. The following also clones the arrays immediately so we don't modify them:



          // filter to within-budget items, sort the two arrays (ascending)
          keyboards = keyboards.Where(k => k < budget).ToArray();
          Array.Sort(keyboards);

          drives = drives.Where(d => d < budget).ToArray();
          Array.Sort(drives);



          J_H has already described how you can get the optimal time complexity, but you can perform the loops at the end very simply, without needing nesting or binary search or any of that.



          You also don't need to record a list of all the candidates, just keep track of the current best, as Henrik Hansen has already demonstrated:



          // maximum within budget price
          int max = -1;


          If we start by looking at the most expensive keyboard and cheapest drive and simultaneously iterate through both, we can do this bit in linear time.



          int ki = keyboards.Length - 1; // drive index
          int di = 0; // drive index

          while (ki >= 0 && di < drives.Length)

          int candidate = keyboards[ki] + drives[di];
          if (candidate <= budget)

          max = Math.Max(candidate, max);
          di++;

          else

          ki--;




          Suppose we are looking at keyboard ki and drive di: candidate is the sum of their costs. If this candidate cost is no more than the budget, then it is a candidate for the max. We also know that we can check for a more pairing by looking at the next most expensive drive, di + 1. If instead the candidate was out of the budget, we know we can find a cheaper candidate by looking at the next cheapest keyboard ki - 1.



          Basically, we look at each keyboard in turn, and cycle through the drives until we find the most expensive one we can get away with. When we find the first drive that is too expensive, we move onto the next keyboard. We know that we don't want any drive cheaper than the last one we looked at, because that could only produce a cheaper pair, so we can continue our search starting from the same drive.



          At the end, we just return max: if we didn't find any candidates below budget, it will still be -1:



          return max;



          Concerning dfhwze's comment about buying more than 2 items: this process is essentially searching the Pareto front, which is done trivially and efficiently for 2 items, but becomes nightmarish for any more, so I would certainly forgive you for sticking to 2 lists ;)




          The above code all in one, with added inline documentation to make the purpose explicit (useful for the consumer, so that they know exactly what it is meant to do, and useful for the maintainer, so that they also know what it is meant to do):



          /// <summary>
          /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
          /// Returns -1 if no pair is within budget.
          /// </summary>
          /// <param name="keyboardPrices">A list of prices of keyboards.</param>
          /// <param name="drivepricess">A list of prices of drives.</param>
          /// <param name="budget">The maximum budget. Must be non-negative</param>
          public static int GetMoneySpent2(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

          if (budget < 0)
          throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
          if (keyboardPrices == null)
          throw new ArgumentNullException(nameof(keyboardPrices));
          if (drivePrices == null)
          throw new ArgumentNullException(nameof(drivePrices));

          if (budget == 0)
          return -1;

          // filter to within-budget items, sort the two arrays (ascending)
          var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
          Array.Sort(keyboards);

          var drives = drivePrices.Where(d => d < budget).ToArray();
          Array.Sort(drives);

          // maximum within budget price
          int max = -1;

          int ki = keyboards.Length - 1; // keyboard index
          int di = 0; // drive index

          while (ki >= 0 && di < drives.Length)

          int candidate = keyboards[ki] + drives[di];
          if (candidate <= budget)

          max = Math.Max(candidate, max);
          di++;

          else

          ki--;



          return max;




          J_H's solution (using a BinarySearch) could well be better in practise, because you only need to sort (and binary search) the shortest input: you can scan the other however you like. Implementation of that, since I too enjoy the sport:



          /// <summary>
          /// Returns the maximum price of any pair of a keyboard and drive that is no more than the given budget.
          /// Returns -1 if no pair is within budget.
          /// </summary>
          /// <param name="keyboardPrices">A list of prices of keyboards.</param>
          /// <param name="drivepricess">A list of prices of drives.</param>
          /// <param name="budget">The maximum budget. Must be non-negative</param>
          public static int GetMoneySpent3(IEnumerable<int> keyboardPrices, IEnumerable<int> drivePrices, int budget)

          if (budget < 0)
          throw new ArgumentOutOfRangeException(nameof(budget), "Budget must be non-negative");
          if (keyboardPrices == null)
          throw new ArgumentNullException(nameof(keyboardPrices));
          if (drivePrices == null)
          throw new ArgumentNullException(nameof(drivePrices));

          if (budget == 0)
          return -1;

          // filter to within-budget items
          var keyboards = keyboardPrices.Where(k => k < budget).ToArray();
          var drives = drivePrices.Where(d => d < budget).ToArray();

          // determine which list is shorter
          IReadOnlyList<int> longList;
          int[] shortList;

          if (keyboards.Length < drives.Length)

          shortList = keyboards;
          longList = drives;

          else

          shortList = drives;
          longList = keyboards;


          // special case of empty short-list
          if (shortList.Length == 0)
          return -1;

          // sort shortList, to facilitate binary search
          Array.Sort(shortList);

          // maximum within budget price
          int max = -1;

          foreach (var k in longList)

          // filter faster
          if (k + shortList[0] > budget)
          continue;

          // find most expensive drive no more than budget - k
          int i = Array.BinarySearch(shortList, budget - k);
          i = i >= 0
          ? i // found
          : ~i - 1; // not found, consider next smallest

          // if such a drive exists, consider it a candidate
          if (i >= 0)

          int candidate = k + shortList[i];
          max = Math.Max(max, candidate);



          return max;







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 15 hours ago









          dfhwze

          4,6511 gold badge7 silver badges34 bronze badges




          4,6511 gold badge7 silver badges34 bronze badges










          answered yesterday









          VisualMelonVisualMelon

          4,72112 silver badges30 bronze badges




          4,72112 silver badges30 bronze badges





















              1












              $begingroup$

              As others have pointed out, the code should be object oriented. It’s OK to start with procedural code as you have, but if you do, you should get in the habit of writing a quick test in your main entry point. Once you’ve written the first test, it can help you start seeing objects more clearly (and drive you to further unit tests.)



              For example: an ElectronicsShop would only have a catalog of items, but it has nothing to do with your spending habits. In your case, it would serve only as a data store for items.



              I’d really expect to see a Shopper class. The shopper would have both Money and a BuyingStrategy. The money could be a simple amount, but the strategy would be what you’re really working on. Finally, the strategy would expose a method Shop(Store, Items[], Budget).



              Inside Shop(), you’d retrieve the items from the store that meet your criteria: they’d be only keyboards and drives, and they’d be within budget. The shopper would add only eligible items to their comparison lists. Then comes time to evaluate them , so you’d add an Evaluate(Budget, ItemLists[]) method that would be called from within Shop(). Inside Evaluate() you can order results by price. But what happens when you get multiple answers that meet the same amount? A budget of 10 would be met by any of 9,1, 1,9, 6,4, or even 5,5! Which is more important, expensive drives or expensive keyboards? (In the real world, you’d go back to your product owner at this point and ask them about their priorities: “do you prefer the most expensive drive, the most expensive keyboard, or should it try to split the difference somehow?”) This might lead you to conclude that Evaluating is really the strategy, not Buying.



              I could go on, but I think I’ve made my point. Notice how once I’ve defined just a few relevant objects that the process I’m describing begins to mirror the real world act of shopping? And once you start down this path of viewing coding problems as models of real objects interacting in the real world, you’ll see the ease of defining objects and writing tests for them, and using them to recognize shortcomings in the specs and completing your specifications. (Pro tip: specifications are always incomplete.)



              Performance isn’t always the best starting point. Object oriented programming is less about finding the most mathematically efficient solution; it’s about building understandable components and proving that they meet your clients’ needs. Getting to the right answer is much more important than quickly getting to some answer that you can’t prove is correct. If performance is an issue after you’ve solved the problem, then start optimizing; but don’t start there.






              share|improve this answer











              $endgroup$

















                1












                $begingroup$

                As others have pointed out, the code should be object oriented. It’s OK to start with procedural code as you have, but if you do, you should get in the habit of writing a quick test in your main entry point. Once you’ve written the first test, it can help you start seeing objects more clearly (and drive you to further unit tests.)



                For example: an ElectronicsShop would only have a catalog of items, but it has nothing to do with your spending habits. In your case, it would serve only as a data store for items.



                I’d really expect to see a Shopper class. The shopper would have both Money and a BuyingStrategy. The money could be a simple amount, but the strategy would be what you’re really working on. Finally, the strategy would expose a method Shop(Store, Items[], Budget).



                Inside Shop(), you’d retrieve the items from the store that meet your criteria: they’d be only keyboards and drives, and they’d be within budget. The shopper would add only eligible items to their comparison lists. Then comes time to evaluate them , so you’d add an Evaluate(Budget, ItemLists[]) method that would be called from within Shop(). Inside Evaluate() you can order results by price. But what happens when you get multiple answers that meet the same amount? A budget of 10 would be met by any of 9,1, 1,9, 6,4, or even 5,5! Which is more important, expensive drives or expensive keyboards? (In the real world, you’d go back to your product owner at this point and ask them about their priorities: “do you prefer the most expensive drive, the most expensive keyboard, or should it try to split the difference somehow?”) This might lead you to conclude that Evaluating is really the strategy, not Buying.



                I could go on, but I think I’ve made my point. Notice how once I’ve defined just a few relevant objects that the process I’m describing begins to mirror the real world act of shopping? And once you start down this path of viewing coding problems as models of real objects interacting in the real world, you’ll see the ease of defining objects and writing tests for them, and using them to recognize shortcomings in the specs and completing your specifications. (Pro tip: specifications are always incomplete.)



                Performance isn’t always the best starting point. Object oriented programming is less about finding the most mathematically efficient solution; it’s about building understandable components and proving that they meet your clients’ needs. Getting to the right answer is much more important than quickly getting to some answer that you can’t prove is correct. If performance is an issue after you’ve solved the problem, then start optimizing; but don’t start there.






                share|improve this answer











                $endgroup$















                  1












                  1








                  1





                  $begingroup$

                  As others have pointed out, the code should be object oriented. It’s OK to start with procedural code as you have, but if you do, you should get in the habit of writing a quick test in your main entry point. Once you’ve written the first test, it can help you start seeing objects more clearly (and drive you to further unit tests.)



                  For example: an ElectronicsShop would only have a catalog of items, but it has nothing to do with your spending habits. In your case, it would serve only as a data store for items.



                  I’d really expect to see a Shopper class. The shopper would have both Money and a BuyingStrategy. The money could be a simple amount, but the strategy would be what you’re really working on. Finally, the strategy would expose a method Shop(Store, Items[], Budget).



                  Inside Shop(), you’d retrieve the items from the store that meet your criteria: they’d be only keyboards and drives, and they’d be within budget. The shopper would add only eligible items to their comparison lists. Then comes time to evaluate them , so you’d add an Evaluate(Budget, ItemLists[]) method that would be called from within Shop(). Inside Evaluate() you can order results by price. But what happens when you get multiple answers that meet the same amount? A budget of 10 would be met by any of 9,1, 1,9, 6,4, or even 5,5! Which is more important, expensive drives or expensive keyboards? (In the real world, you’d go back to your product owner at this point and ask them about their priorities: “do you prefer the most expensive drive, the most expensive keyboard, or should it try to split the difference somehow?”) This might lead you to conclude that Evaluating is really the strategy, not Buying.



                  I could go on, but I think I’ve made my point. Notice how once I’ve defined just a few relevant objects that the process I’m describing begins to mirror the real world act of shopping? And once you start down this path of viewing coding problems as models of real objects interacting in the real world, you’ll see the ease of defining objects and writing tests for them, and using them to recognize shortcomings in the specs and completing your specifications. (Pro tip: specifications are always incomplete.)



                  Performance isn’t always the best starting point. Object oriented programming is less about finding the most mathematically efficient solution; it’s about building understandable components and proving that they meet your clients’ needs. Getting to the right answer is much more important than quickly getting to some answer that you can’t prove is correct. If performance is an issue after you’ve solved the problem, then start optimizing; but don’t start there.






                  share|improve this answer











                  $endgroup$



                  As others have pointed out, the code should be object oriented. It’s OK to start with procedural code as you have, but if you do, you should get in the habit of writing a quick test in your main entry point. Once you’ve written the first test, it can help you start seeing objects more clearly (and drive you to further unit tests.)



                  For example: an ElectronicsShop would only have a catalog of items, but it has nothing to do with your spending habits. In your case, it would serve only as a data store for items.



                  I’d really expect to see a Shopper class. The shopper would have both Money and a BuyingStrategy. The money could be a simple amount, but the strategy would be what you’re really working on. Finally, the strategy would expose a method Shop(Store, Items[], Budget).



                  Inside Shop(), you’d retrieve the items from the store that meet your criteria: they’d be only keyboards and drives, and they’d be within budget. The shopper would add only eligible items to their comparison lists. Then comes time to evaluate them , so you’d add an Evaluate(Budget, ItemLists[]) method that would be called from within Shop(). Inside Evaluate() you can order results by price. But what happens when you get multiple answers that meet the same amount? A budget of 10 would be met by any of 9,1, 1,9, 6,4, or even 5,5! Which is more important, expensive drives or expensive keyboards? (In the real world, you’d go back to your product owner at this point and ask them about their priorities: “do you prefer the most expensive drive, the most expensive keyboard, or should it try to split the difference somehow?”) This might lead you to conclude that Evaluating is really the strategy, not Buying.



                  I could go on, but I think I’ve made my point. Notice how once I’ve defined just a few relevant objects that the process I’m describing begins to mirror the real world act of shopping? And once you start down this path of viewing coding problems as models of real objects interacting in the real world, you’ll see the ease of defining objects and writing tests for them, and using them to recognize shortcomings in the specs and completing your specifications. (Pro tip: specifications are always incomplete.)



                  Performance isn’t always the best starting point. Object oriented programming is less about finding the most mathematically efficient solution; it’s about building understandable components and proving that they meet your clients’ needs. Getting to the right answer is much more important than quickly getting to some answer that you can’t prove is correct. If performance is an issue after you’ve solved the problem, then start optimizing; but don’t start there.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 2 hours ago

























                  answered 2 hours ago









                  John DetersJohn Deters

                  8465 silver badges9 bronze badges




                  8465 silver badges9 bronze badges



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Code Review Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      Use MathJax to format equations. MathJax reference.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224100%2fhackerrank-electronics-shop%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

                      Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

                      199年 目錄 大件事 到箇年出世嗰人 到箇年死嗰人 節慶、風俗習慣 導覽選單