LinqToExcel to load Dictionary - c#

I have the following worked out but quite less than elegant. I'd like to work this out with ToDictionary if possible. Thank you for any help as I'm pretty new.
var excel = new ExcelQueryFactory(#"E:\MAHipotCepaStationProgram.xlsx");
//get list of program names
List<string> testNames = new List<string>();
testNames.AddRange(excel.Worksheet().ToList()
.Where(s => s["Program #"].Value.ToString() == "Program Title")
.Select(s => s[1].Value.ToString()));
// get list of program numbers
List<int> testNumbers = new List<int>();
testNumbers.AddRange(excel.Worksheet().ToList()
.Where(s => s["Program #"].Value.ToString() == "Program #")
.Select(s => Convert.ToInt32(s[1].Value)));
// combine them
Dictionary<int, string> programs = new Dictionary<int, string>();
for (int x = 0; x < testNames.Count-1; x++)
{
if (!programs.ContainsKey(Convert.ToInt32(testNumbers[x])))
{
programs.Add(Convert.ToInt32(testNumbers[x]), testNames[x]);
}
else
{
testNumbers[x].Dump("Duplicate Found");
}
}
programs.Dump("Dict");
This is as close as I've gotten, but not right. Error:
Requires a receiver of type IEnumerable string
which isn't computing with me:
var excel = new ExcelQueryFactory(#"E:\MAHipotCepaStationProgram.xlsx");
Dictionary<string, string> programsDict = excel.Worksheet().ToDictionary<string, string>(
e => e["Program #"].Value.ToString() == "Program Title")
.Select(s => s[1].Value.ToString()),
f => f.Where(d => d.Value.ToString() == "Program #").ToString());

You can filter the values using a sigle LINQ query.This will return the name and number columns in the excel:
var sampleExcel = new ExcelQueryFactory(#"I:\Book1.xlsx");
var sampleWorksheet = from workSheet in sampleExcel.Worksheet("Sheet1") select workSheet;
var selectedValues = from excelRow in sampleExcel.Worksheet()
select new { name = excelRow[0], number =Convert.ToInt32(excelRow[1]) };
foreach (var item in selectedValues)
{
Console.WriteLine(string.Format("Name is {0} ,number is {1}",item.name,item.number));
}
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach (var item in selectedValues)
{
dict.Add(item.number, item.name);
Console.WriteLine(string.Format("Name is {0} ,number is {1}", item.name, item.number));
}
Equivalent lambda expression for the above LINQ query:
var selectedValues1 = sampleExcel.Worksheet().Select(x => new { name = x[0], number = x[1] });

Have a go at this:
Dictionary<int, string> programsDict =
excel
.Worksheet()
.Select(x => new { A = x[0].ToString(), B = x[1].ToString() })
.ToArray()
.Where(x => new [] { "Program #", "Program Title" }.Contains(x.A))
.Buffer(2)
.Select(x => new { title = x[0].B, number = int.Parse(x[1].B) })
.ToDictionary(x => x.number, x => x.title);
You just need to NuGet "System.Interactive" to get the .Buffer(int) operator.
Or use this implementation:
public static IEnumerable<T[]> Buffer<T>(this IEnumerable<T> source, int count)
=>
source
.Select((t, i) => new { t, i })
.GroupBy(x => x.i / count)
.Select(x => x.Select(y => y.t).ToArray());

Related

Converting Tuple<List<Guid>, string> to Dictionary<Guid, List<string>>

I am trying to converting a Tuple<List<Guid>, string> to Dictionary<Guid, List<string>>. This is what I have so far:
var listOfTuples = GetListOfTuples(); // returns type List<Tuple<List<Guid>, string>>
var transformedDictionary = new Dictionary<Guid, List<string>>();
foreach (var listOfTuple in listOfTuples)
{
foreach (var key in listOfTuple.Item1)
{
if (!transformedDictionary.ContainsKey(key))
transformedDictionary[key] = new List<string> { listOfTuple.Item2 };
else transformedDictionary[key].Add(listOfTuple.Item2);
}
}
Is there a better way of doing this, perhaps using LINQ; SelectMany, Grouping, or toDictionary?
Update: I have tried this, but clearly not working:
listOfTuples.ToList()
.SelectMany(x => x.Item1,(y, z) => new { key = y.Item2, value = z })
.GroupBy(p => p.key)
.ToDictionary(x => x.Key, x => x.Select(m => m.key));
You are close. The problem is with selecting the right key and value
var result = listOfTuples.SelectMany(t => t.Item1.Select(g => (g, str: t.Item2)))
.GroupBy(item => item.g, item => item.str)
.ToDictionary(g => g.Key, g => g.ToList());
The mistake is here (y, z) => new { key = y.Item2, value = z } - you want the key to be the Guid and therefore instead of it being Item2 it should be z which is the Guid. So you can go with the way I wrote it or just
(y, z) => new { key = z, value = y.Item2 }
Also the .ToList() at the beginning is not needed. You say that listOfTuples already returns a list

Turning a Dictionary<Guid,IList<String>> into Dictionary<string,IList<Guid>> With LINQ?

I have a Dictionary<Guid,IList<string>> which shows all the names an entity can have.
I want to convert this to see all the names mapped to all the entities.
so:
[["FFF" => "a", "b"],
["EEE" => "a", "c"]]
Becomes
[["a" => "FFF", "EEE"],
["b" => "FFF"],
["c" => "EEE"]]
I know this is easy to do with foreaches but I'm wondering if there is a way with LINQ / ToDictionary?
private static void Main(string[] args)
{
var source = new Dictionary<Guid, IList<string>>
{
{ Guid.NewGuid(), new List<string> { "a", "b" } },
{ Guid.NewGuid(), new List<string> { "b", "c" } },
};
var result = source
.SelectMany(x => x.Value, (x, y) => new { Key = y, Value = x.Key })
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(y => y.Value).ToList());
foreach (var item in result)
{
Console.WriteLine($"Key: {item.Key}, Values: {string.Join(", ", item.Value)}");
}
}
var dic = new Dictionary<string, List<string>>()
{
{"FFF", new List<string>(){"a", "b"}},
{"EEE", new List<string>(){"a", "c"}}
};
var res = dic.SelectMany(x => x.Value, (x,y) => new{Key = y, Value = x.Key})
.ToLookup(x => x.Key, x => x.Value);
Dictionary<int,IList<string>> d = new Dictionary<int ,IList<string>>(){
{1,new string[]{"a","b"}},
{2,new string[]{"a","d"}},
{3,new string[]{"b","c"}},
{4,new string[]{"x","y"}}};
d.SelectMany(kvp => kvp.Value.Select(element => new { kvp.Key, element}))
.GroupBy(g => g.element, g => g.Key)
.ToDictionary(g => g.Key, g => g.ToList());

Avoid Repeating Group by Linq in C#

I need to optimize My Code. I Have Some Repeating Code. But I would like to optimize it. Can any one please help me to optimize My Code. How Can I greate Common Function For this???
foreach (var item in hotellocation.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count()))
{
if (item.Key != "")
{
lstHotelLocation.Add(new HotelLocation()
{
Name = item.Key,
count = item.Value
});
}
}
//need to Apply to linq
foreach (var item in hoteltype.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count()))
{
if (item.Key != "")
{
lstHotelType.Add(new HotelTypeFilter()
{
Name = item.Key,
count = item.Value
});
}
}
The first thing to do is get rid of those foreach loops, as they are incongruous with LINQ, and ditch the dictionary, since it's pointless:
var lstHotelLocation = hotellocation.GroupBy(x => x)
.Where(g => g.Key != "")
.Select(g => new HotelLocation {
Name = kv.Key,
count = g.Count()
})
.ToList();
var lstHotelType = hoteltype.GroupBy(x => x)
.Where(g => g.Key != "")
.Select(g => new HotelTypeFilter {
Name = g.Key,
count = g.Count()
})
.ToList();
If you want to further remove the duplication, you can do this:
static List<T> AssembleCounts<T>(IEnumerable<string> values,
Func<string, int, T> makeObject)
{
return values.Where(x => !string.IsNullOrEmpty(x))
.GroupBy(x => x)
.Select(g => makeObject(g.Key, g.Count()))
.ToList();
}
var lstHotelLocation = AssembleCounts(hotellocation,
(k, c) => new HotelLocation {
Name = k, count = c
});
var lstHotelType = AssembleCounts(hoteltype,
(k, c) => new HotelTypeFilter {
Name = k, count = c
});

Crafting a LINQ based solution to determine if a set of predicates are satisfied for a pair of collections constrained by a set of invariants

This isn't a question I feel I have the vocabulary to properly express, but I have two collections of the same anonymous type (lets call it 'a.)
'a is defined as new {string Name, int Count}
One of these collections of 'a we shall call requirements.
One of these collections of 'a we shall call candidates.
Given these collections, I want to determine if the following assertions hold.
If there exists some element in requirements r such that r.Count == 0, each element in candidates c such that r.Name == c.Name must satisfy c.Count == 0. There must exist one such element in candidates for each such element in requirements.
For each element of requirements r where r.Count > 0, there must be some subset of elements in candidates c such that c₁.Name, c₂.Name, ..., cₓ.Name == r.Name and that c₁ + ... + cₓ >= r.Count. Each element of candidates used to satisfy this rule for some element in requirements may not be used for another element in requirements.
An example of this would be that given
requirements = {{"A",0}, {"B", 0}, {"C", 9}}
candidates = {{"B", 0}, {"C", 1}, {"A",0}, {"D", 2}, {"C", 4}, {"C", 4}}
That this query would be satisfied.
r={"A", 0} and r={"B", 0} would be satisfied according to rule #1 against c={"A", 0} and c={"B", 0}
-and-
r={"C", 9) is satisfied according to rule #2 by the group gc on collections c.Name derived from {{"C", 1}, {"C", 4}, {"C", 4}} as gc = {"C", 9}
However it is worth noting that if requirements contained {"C", 6} and {"C", 3} instead of {"C", 9}, this particular set of collections would fail to satisfy the predicates.
Now to the question finally.
What is the best way to form this into a linq expression prioritizing speed (least iterations)?
The unsolved subset has been re-asked here
Here's my sketch for a linqy solution, but it doesn't address #3 at all. It works by grouping and joining on names. The hard part would then be to determine if there is some matching of requirements to candidates that satisfies the group.
void Main() {
var requirements = new [] {
new NameCount{ Name = "A", Count = 0 },
new NameCount{ Name = "B", Count = 0 },
new NameCount{ Name = "C", Count = 9 },
new NameCount{ Name = "D", Count = 3 },
new NameCount{ Name = "D", Count = 5 },
};
var candidates = new[] {
new NameCount {Name = "B", Count = 0},
new NameCount {Name = "C", Count = 1},
new NameCount {Name = "A", Count = 0},
new NameCount {Name = "D", Count = 2},
new NameCount {Name = "C", Count = 4},
new NameCount {Name = "C", Count = 4}
};
var matched = requirements
.GroupBy(r => r.Name)
.GroupJoin(candidates, rg => rg.Key, c => c.Name,
(rg, cg) => new { requirements = rg, candidates = cg });
bool satisfied = matched.All( /* ??? */ );
}
struct NameCount {
public string Name;
public int Count;
}
For the given input, matched would be this:
.GroupJoin has much better performance characteristics than candidates.Where in the projection.
After reconsidering the revised requirements, I've come up with a invariant assertions that must hold for a solution to exist..
For each paired cg and rg...
|cg.Name| >= |rg.Name|
cg.SummedCount >= rg.SummedCount
Assuming we have satisfied those conditions, a solution MAY exist.
My intuition suggests something similar to the following algorithm:
For each Name...
Let us call each r in rg a basket, and each c in cg an apple.
Sort apples in descending order.
We will keep track of which elements we've assigned to each basket in rg (e.g. r₁ is paired with cg₁.) Maintain sortedness in our buckets by ascending order of rₓ.Count - cgₓ.Count. (This value may be negative.)
Now, iterate through our list of apples, starting with the largest, and assign it to the least filled bucket by iterating through rg. If we overfill the first bucket, we continue descending through the list until we encounter a bucket that would remain unfilled if we put that apple in it. We then choose the previous bucket.
That is, we want to minimize the number of apples necessary to fill each bucket, so we prefer a perfect fit to overfilling, and overfilling to underfilling.
This algorithm does not work on the following case:
rg = (6, 5), cg = (3, 2, 2, 2, 2)
The above algorithm produces
r6 = (3, 2, 2), r5 = (2, 2)
whereas the solution ought to be
r6 = (2, 2, 2), r5 = (3, 2)
Going to post the obvious answer here, but I'm looking for something more elegant.
Given candidates as IEnumerable<'a>, project IEnumerable<'a> groupedCandidates from candidates by calling candidates.Where(c=>c.Count != 0).GroupBy(...) by performing a Sum on all elements with the same name.
Then project simpleCandidates from candidates.Except(groupedCandidates, (c,gc)=>c.Name == gc.Name)
Past here it gets fuzzy because candidates may only satisfy a requirement once.
EDIT: This solution does not meet the revised requirements.
I'm not familiar with LINQ, but it looks like you can do this problem in O(n) unless I misunderstand something. There are three steps to completing this problem.
First, construct a list or hashtable counter and populate it by iterating through c. If we use a hashtable, the size of the hashtable will be the length of c so we don't have to resize our hashtable.
for candidate in c:
counter[candidate.name] += candidate.count
We do this in one pass. O(m) where m is the length of c.
With counter constructed, we construct a hashtable by iterating through r.
for requirement in r:
if not h[requirement.name] or not requirement.count >= h[requirement.name]:
h[requirement.name] = requirement.count
Then, we simply iterate through counter and compare counts.
for sum in counter:
assert h[sum.name] and h[sum.name] >= sum.count
We do this in one pass: O(p) where p is the length of counter.
If this algorithm terminates successfully, our constraints are satisfied, and we've completed it in O(m) + O(o) + O(p)
I finally came up with a workable solution
IEnumerable<Glyph> requirements = t.Objectives.Cast<Glyph>().OrderBy(k => k.Name);
IEnumerable<Glyph> candidates = Resources.Cast<Glyph>().OrderBy(k => k.Name);
IEnumerable<Glyph> zeroCountCandidates = candidates.Where(c => c.Count == 0);
IEnumerable<Glyph> zeroCountRequirements = requirements.Where(r => r.Count == 0);
List<Glyph> remainingCandidates = zeroCountCandidates.ToList();
if (zeroCountCandidates.Count() < zeroCountRequirements.Count())
{
return false;
}
foreach (var r in zeroCountRequirements)
{
if (!remainingCandidates.Contains(r))
{
return false;
}
else
{
remainingCandidates.Remove(r);
}
}
IEnumerable<Glyph> nonZeroCountCandidates = candidates.Where(c => c.Count > 0);
IEnumerable<Glyph> nonZeroCountRequirements = requirements.Where(r => r.Count > 0);
var perms = nonZeroCountCandidates.Permutations();
foreach (var perm in perms)
{
bool isViableSolution = true;
remainingCandidates = perm.ToList();
foreach (var requirement in nonZeroCountRequirements)
{
int countThreshold = requirement.Count;
while (countThreshold > 0)
{
if (remainingCandidates.Count() == 0)
{
isViableSolution = false;
break;
}
var c = remainingCandidates[0];
countThreshold -= c.Count;
remainingCandidates.Remove(c);
}
}
if (isViableSolution)
{
return true;
}
}
return false;
Disgusting isn't it?
algorithm:
if any requirement Name doesn't exist in the candidates, return false
for any requirement having Count = 0
if there aren't at least as many candidates
with the same Name and Count, return false
eliminate all exact matches between candidates and requirements
eliminate requirements (and candidates) where the requirement
and all higher requirements have a higher candidate available
for remaining non-zero requirements
find the subset of candidates
that matches the most requirements
and eliminate the requirements (and candidates)
if there are any remaining non-zero requirements
return false
return true because no unmatched requirements remain
sample implementation:
public static bool IsValid(IEnumerable<string> requirementNames,
IList<int> requirementCounts,
IEnumerable<string> candidateNames,
IList<int> candidateCounts)
{
var requirements = requirementNames
.Select((x, i) => new
{
Name = x,
Count = requirementCounts[i]
})
.ToList();
var candidates = candidateNames
.Select((x, i) => new
{
Name = x,
Count = candidateCounts[i]
})
.ToList();
var zeroRequirements = requirements
.Where(x => x.Count == 0)
.Select(x => x.Name)
.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
var zeroCandidates = candidates
.Where(x => x.Count == 0)
.Select(x => x.Name)
.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
if (zeroRequirements.Keys.Any(x => !zeroCandidates.ContainsKey(x) ||
zeroCandidates[x] < zeroRequirements[x]))
{
return false;
}
var nonZeroRequirements = requirements
.Where(x => x.Count != 0)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key,
x => x.Select(y => y.Count)
.GroupBy(y => y)
.ToDictionary(y => y.Key, y => y.Count()));
var nonZeroCandidates = candidates
.Where(x => x.Count != 0)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key,
x => x.Select(y => y.Count)
.GroupBy(y => y)
.ToDictionary(y => y.Key, y => y.Count()));
foreach (var name in nonZeroRequirements.Keys.ToList())
{
var requirementsForName = nonZeroRequirements[name];
Dictionary<int, int> candidatesForName;
if (!nonZeroCandidates.TryGetValue(name, out candidatesForName))
{
return false;
}
if (candidatesForName.Sum(x => x.Value) <
requirementsForName.Sum(x => x.Value))
{
return false;
}
if (candidatesForName.Sum(x => x.Value*x.Key) <
requirementsForName.Sum(x => x.Value*x.Key))
{
return false;
}
EliminateExactMatches(candidatesForName, requirementsForName);
EliminateHighRequirementsWithAvailableHigherCandidate(candidatesForName, requirementsForName);
EliminateRequirementsThatHaveAMatchingCandidateSum(candidatesForName, requirementsForName);
if (requirementsForName
.Any(x => x.Value > 0))
{
return false;
}
}
return true;
}
private static void EliminateRequirementsThatHaveAMatchingCandidateSum(
IDictionary<int, int> candidatesForName,
IDictionary<int, int> requirementsForName)
{
var requirements = requirementsForName
.Where(x => x.Value > 0)
.OrderByDescending(x => x.Key)
.SelectMany(x => Enumerable.Repeat(x.Key, x.Value))
.ToList();
if (!requirements.Any())
{
return;
}
// requirements -> candidates used
var items = GenerateCandidateSetsThatSumToOrOverflow(
requirements.First(),
candidatesForName,
new List<int>())
.Concat(new[] {new KeyValuePair<int, IList<int>>(0, new List<int>())})
.Select(x => new KeyValuePair<IList<int>, IList<int>>(
new[] {x.Key}, x.Value));
foreach (var count in requirements.Skip(1))
{
var count1 = count;
items = (from i in items
from o in GenerateCandidateSetsThatSumToOrOverflow(
count1,
candidatesForName,
i.Value)
select
new KeyValuePair<IList<int>, IList<int>>(
i.Key.Concat(new[] {o.Key}).OrderBy(x => x).ToList(),
i.Value.Concat(o.Value).OrderBy(x => x).ToList()))
.GroupBy(
x => String.Join(",", x.Key.Select(y => y.ToString()).ToArray()) + ">"
+ String.Join(",", x.Value.Select(y => y.ToString()).ToArray()))
.Select(x => x.First());
}
var bestSet = items
.OrderByDescending(x => x.Key.Count(y => y > 0)) // match the most requirements
.ThenByDescending(x => x.Value.Count) // use the most candidates
.ToList();
var best = bestSet.First();
foreach (var requirementCount in best.Key.Where(x => x > 0))
{
requirementsForName[requirementCount] -= 1;
}
foreach (var candidateCount in best.Value.Where(x => x > 0))
{
candidatesForName[candidateCount] -= 1;
}
}
private static void EliminateHighRequirementsWithAvailableHigherCandidate(
IDictionary<int, int> candidatesForName,
IDictionary<int, int> requirementsForName)
{
foreach (var count in requirementsForName
.Where(x => x.Value > 0)
.OrderByDescending(x => x.Key)
.Select(x => x.Key)
.ToList())
{
while (requirementsForName[count] > 0)
{
var count1 = count;
var largerCandidates = candidatesForName
.Where(x => x.Key > count1)
.OrderByDescending(x => x.Key)
.ToList();
if (!largerCandidates.Any())
{
return;
}
var largerCount = largerCandidates.First().Key;
requirementsForName[count] -= 1;
candidatesForName[largerCount] -= 1;
}
}
}
private static void EliminateExactMatches(
IDictionary<int, int> candidatesForName,
IDictionary<int, int> requirementsForName)
{
foreach (var count in requirementsForName.Keys.ToList())
{
int numberOfCount;
if (candidatesForName.TryGetValue(count, out numberOfCount) &&
numberOfCount > 0)
{
var toRemove = Math.Min(numberOfCount, requirementsForName[count]);
requirementsForName[count] -= toRemove;
candidatesForName[count] -= toRemove;
}
}
}
private static IEnumerable<KeyValuePair<int, IList<int>>> GenerateCandidateSetsThatSumToOrOverflow(
int sumNeeded,
IEnumerable<KeyValuePair<int, int>> candidates,
IEnumerable<int> usedCandidates)
{
var usedCandidateLookup = usedCandidates
.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
var countToIndex = candidates
.Select(x => Enumerable.Range(
0,
usedCandidateLookup.ContainsKey(x.Key)
? x.Value - usedCandidateLookup[x.Key]
: x.Value)
.Select(i => new KeyValuePair<int, int>(x.Key, i)))
.SelectMany(x => x)
.ToList();
// sum to List of <count,index>
var sumToElements = countToIndex
.Select(a => new KeyValuePair<int, IList<KeyValuePair<int, int>>>(
a.Key, new[] {a}))
.ToList();
countToIndex = countToIndex.Where(x => x.Key < sumNeeded).ToList();
while (sumToElements.Any())
{
foreach (var set in sumToElements
.Where(x => x.Key >= sumNeeded))
{
yield return new KeyValuePair<int, IList<int>>(
sumNeeded,
set.Value.Select(x => x.Key).ToList());
}
sumToElements = (from a in sumToElements.Where(x => x.Key < sumNeeded)
from b in countToIndex
where !a.Value.Any(x => x.Key == b.Key && x.Value == b.Value)
select new KeyValuePair<int, IList<KeyValuePair<int, int>>>(
a.Key + b.Key,
a.Value.Concat(new[] {b})
.OrderBy(x => x.Key)
.ThenBy(x => x.Value)
.ToList()))
.GroupBy(x => String.Join(",", x.Value.Select(y => y.Key.ToString()).ToArray()))
.Select(x => x.First())
.ToList();
}
}
private static IEnumerable<int> GetAddendsFor(int sum, Random random)
{
var values = new List<int>();
while (sum > 0)
{
var addend = random.Next(1, sum);
sum -= addend;
values.Add(addend);
}
return values;
}
Tests:
[Test]
public void ABCC_0063__with_candidates__BCADCC_010244__should_return_false()
{
var requirementNames = "ABCC".Select(x => x.ToString()).ToArray();
var requirementCounts = new[] {0, 0, 6, 3};
var candidateNames = "BCADCC".Select(x => x.ToString()).ToArray();
var candidateCounts = new[] {0, 1, 0, 2, 4, 4};
var actual = IsValid(requirementNames, requirementCounts, candidateNames, candidateCounts);
actual.ShouldBeFalse();
}
[Test]
public void ABC_003__with_candidates__BCADCC_010244__should_return_true()
{
var requirementNames = "ABC".Select(x => x.ToString()).ToArray();
var requirementCounts = new[] {0, 0, 3};
var candidateNames = "BCADCC".Select(x => x.ToString()).ToArray();
var candidateCounts = new[] {0, 1, 0, 2, 4, 4};
var actual = IsValid(requirementNames, requirementCounts, candidateNames, candidateCounts);
actual.ShouldBeTrue();
}
[Test]
public void ABC_003__with_candidates__BCAD_0102__should_return_false()
{
var requirementNames = "ABC".Select(x => x.ToString()).ToArray();
var requirementCounts = new[] {0, 0, 3};
var candidateNames = "BCAD".Select(x => x.ToString()).ToArray();
var candidateCounts = new[] {0, 1, 0, 2};
var actual = IsValid(requirementNames, requirementCounts, candidateNames, candidateCounts);
actual.ShouldBeFalse();
}
[Test]
public void ABC_009__with_candidates__BCADCC_010244__should_return_true()
{
var requirementNames = "ABC".Select(x => x.ToString()).ToArray();
var requirementCounts = new[] {0, 0, 9};
var candidateNames = "BCADCC".Select(x => x.ToString()).ToArray();
var candidateCounts = new[] {0, 1, 0, 2, 4, 4};
var actual = IsValid(requirementNames, requirementCounts, candidateNames, candidateCounts);
actual.ShouldBeTrue();
}
[Test]
public void FuzzTestIt()
{
var random = new Random();
const string names = "ABCDE";
for (var tries = 0; tries < 10000000; tries++)
{
var numberOfRequirements = random.Next(5);
var shouldPass = true;
var requirementNames = new List<string>();
var requirementCounts = new List<int>();
var candidateNames = new List<string>();
var candidateCounts = new List<int>();
for (var i = 0; i < numberOfRequirements; i++)
{
var name = names.Substring(random.Next(names.Length), 1);
switch (random.Next(6))
{
case 0: // zero-requirement with corresponding candidate
requirementNames.Add(name);
requirementCounts.Add(0);
candidateNames.Add(name);
candidateCounts.Add(0);
break;
case 1: // zero-requirement without corresponding candidate
requirementNames.Add(name);
requirementCounts.Add(0);
shouldPass = false;
break;
case 2: // non-zero-requirement with corresponding candidate
{
var count = random.Next(1, 10);
requirementNames.Add(name);
requirementCounts.Add(count);
candidateNames.Add(name);
candidateCounts.Add(count);
}
break;
case 3: // non-zero-requirement with matching sum of candidates
{
var count = random.Next(1, 10);
requirementNames.Add(name);
requirementCounts.Add(count);
foreach (var value in GetAddendsFor(count, random))
{
candidateNames.Add(name);
candidateCounts.Add(value);
}
}
break;
case 4: // non-zero-requirement with matching overflow candidate
{
var count = random.Next(1, 10);
requirementNames.Add(name);
requirementCounts.Add(count);
candidateNames.Add(name);
candidateCounts.Add(count + 2);
}
break;
case 5: // non-zero-requirement without matching candidate or sum or candidates
{
var count = random.Next(10, 20);
requirementNames.Add(name);
requirementCounts.Add(count);
shouldPass = false;
}
break;
}
}
try
{
var actual = IsValid(requirementNames, requirementCounts, candidateNames, candidateCounts);
actual.ShouldBeEqualTo(shouldPass);
}
catch (Exception e)
{
Console.WriteLine("Requirements: " + String.Join(", ", requirementNames.ToArray()));
Console.WriteLine(" " +
String.Join(", ", requirementCounts.Select(x => x.ToString()).ToArray()));
Console.WriteLine("Candidates: " + String.Join(", ", candidateNames.ToArray()));
Console.WriteLine(" " +
String.Join(", ", candidateCounts.Select(x => x.ToString()).ToArray()));
Console.WriteLine(e);
Assert.Fail();
}
}
}

Grouping each 500 elements of array according to array elements

I have an array of 2000 strings. The strings are: "art", "economy", "sport" and "politic". I want to group each 500 elements and get their counts
Could anyone help please?
Another solution:
var count = 0;
var dictionaries =
strings.GroupBy(s => count++ / 500)
.Select(g => g.Distinct().ToDictionary(k => k, k => g.Count(s => s == k)))
.ToList();
This will create a List<Dictionary<string, int>>. Each dictionary represents a tally of 500 elements (or possibly less for the last dictionary), where the keys are strings and the values are the number of occurrences of the string among the 500 elements the dictionary represents.
There is no requirement to hardcode all the possible values that may be encountered.
For the maximum possible performance you can also use this version:
var count = 0;
var dictionaries =
strings.GroupBy(s => count++ / 500)
.Select(g => g.Aggregate(
new Dictionary<string, int>(),
(d, w) => { d[w] = (d.ContainsKey(w) ? d[w] + 1 : 1); return d; })
)
.ToList();
This version iterates over each element in your source array exactly once. The output is in the same format as the first version.
var result = strings.Select((s, i) => new { s, i })
.GroupBy(x => x.i / 500)
.Select(x => x.GroupBy(y => y.s)
.Select(z => new {
Name=z.Key,
Count=z.Count()
}).ToList())
.ToList();
Try
var grouping = Enumerable.Range(0,2000)
.Select(i => i / 500)
.Zip(Strings, (i,s) => new { Group = i, Str = s})
.GroupBy(anon => anon.Group,
anon => anon.Str,
(key,g) => new
{
Key = key,
Art = g.Count(str => str == "art"),
Economy = g.Count(str => str == "economy"),
Politic = g.Count(str => str == "politic"),
Sport= g.Count(str => str == "sport")
});
foreach(anon in grouping)
{
//textbox logic OP will have to change to suit
TextBox1.WriteLine(String.Format("Group: {0}", anon.Key));
TextBox1.WriteLine(String.Format("Art: {0}",anon.Art));
TextBox1.WriteLine(String.Format("Economy: {0}",anon.Economy ));
TextBox1.WriteLine(String.Format("Politic: {0}",anon.Politic ));
TextBox1.WriteLine(String.Format("Sport: {0}",anon.Sport));
}
Alternatively (as per Snowbear)
var grouping = Strings.Select((s,i) => new { Group = i / 500, Str = s})
.GroupBy(anon => anon.Group,
anon => anon.Str,
(key,g) => new
{
Key = key,
Art = g.Count(str => str == "art"),
Economy = g.Count(str => str == "economy"),
Politic = g.Count(str => str == "politic"),
Sport= g.Count(str => str == "sport")
});
foreach(anon in grouping)
{
//textbox logic OP will have to change to suit
TextBox1.WriteLine(String.Format("Group: {0}",anon.Key + 1));
TextBox1.WriteLine(String.Format("Art: {0}",anon.Art));
TextBox1.WriteLine(String.Format("Economy: {0}",anon.Economy ));
TextBox1.WriteLine(String.Format("Politic: {0}",anon.Politic ));
TextBox1.WriteLine(String.Format("Sport: {0}",anon.Sport));
}
int CountElementsInGroup = 500;
//from 500 to 1000
int NumberGroup = 2;
string[] GroupTypes = new string[4] { "art", "economy", "sport", "politic" };
//Fill example array
string[] arr = new string[2000];
Random rand = new Random();
for (int i = 0; i < arr.Length;i++ )
arr[i] = GroupTypes[rand.Next(0, 3)];
var res = (from p in arr.Skip((NumberGroup - 1) * CountElementsInGroup).Take(CountElementsInGroup)
group p by p into g
select new GroupCountClass { GroupName = g.Key, GroupCount = g.Count() });
textBox1.Text = "";
foreach (GroupCountClass c in res)
{
textBox1.Text += String.Format("GroupName:{0} Count:{1};",c.GroupName,c.GroupCount);
}

Categories

Resources