Reputation: 3294
Given I have a list of integers like this:
List<int> items = new List<int> {200, 100, 50, 20, 10, 5, 2, 1};
and given I have a number, say 13, how will I find the numbers from the list which adds up to 13 using LINQ (or any other way). The list is always in the descending order.
For example: 13 = 10 + 2+ 1, so the linq operation would give me back a list of integers containing 10,2 and 1.
If we cant find the full match as in the case of 24, it's ok for an exception to be generated.
Effort:
[Test]
public void Should_find_subset()
{
var items = new List<int>() {200, 100, 50, 20, 10, 5, 2, 1};
var find = 13;
var result = new List<int>();
var subset = new List<int>();
bool found = false;
foreach (var item in items)
{
if (item == find)
{
result.Add(item);
found = true;
}
if (item < find)
{
subset.Add(item);
found = subset.Sum() == find;
}
if (found)
break;
}
}
Thanks,
-Mike
Upvotes: 2
Views: 213
Reputation: 460360
If i hear combinations i suggest this project: Permutations, Combinations, and Variations
This is working code:
List<int> items = new List<int> { 200, 100, 50, 20, 10, 5, 2, 1 };
var allMatchingCombos = new List<IList<int>>();
for (int i = 1; i < items.Count; i++)
{
IEnumerable<IList<int>> matchingCombos = new Combinations<int>(items, i, GenerateOption.WithoutRepetition)
.Where(c => c.Sum() == 13);
allMatchingCombos.AddRange(matchingCombos);
}
foreach(var combo in allMatchingCombos)
Console.WriteLine(string.Join(",", combo));
Output: 10,2,1
Edit: Since you have explicitly asked for LINQ, here is a full LINQified approach:
List<IList<int>> allMatchingCombos = Enumerable.Range(1, items.Count)
.SelectMany(i => new Combinations<int>(items, i, GenerateOption.WithoutRepetition)
.Where(c => c.Sum() == 13)
.ToList())
.ToList();
Upvotes: 4
Reputation: 101162
A simply and inefficient approach using Aggregate
:
List<int> items = new List<int> {200, 100, 50, 20, 10, 5, 2, 1};
var target = 373;
var result = items.Aggregate(new List<int>(), (acc, curr) =>
{
if (acc.Sum() + curr <= target)
acc.Add(curr);
return acc;
});
if(result.Sum() != target)
throw new Exception(); // whatever
result:
I should note that such a simply approach will not work for all cases. E.g. List is 68,50,20, and target is 70. This will result in an error instead of 50, 20.
Another inefficient approach that handles such cases:
List<int> items = new List<int> {68, 50, 20};
var target = 70;
var result = new List<int>();
while(result.Sum() != target && items.Any())
{
result = new List<int>();
foreach (var item in items)
if (result.Sum() + item <= target)
result.Add(item);
if(result.Sum() != target)
items.Remove(result.Last());
}
if(result.Sum() != target)
throw new Exception(); // whatever, no solution found
result:
Using a large input list will probably be slow as hell.
Upvotes: 3