Dictionary iterator in c# - c#

I want to know if there is any way to store value of an enumerator of key value pairs while iterating through a dictionary. I want to store the key and value of an enumerator in some variable. What is the solution? What I wanted to do is while iterating through dictionary to have a reference of the current key value pair and the next keyvalue pair in the dictionary.I dont know why it is not working
Here is what the solution might look like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Diagnostics;
namespace WellPuzzle
{
class Solution
{
Hashtable h1 = new Hashtable();
List<int> listofitemstoremove = new List<int>();
Dictionary<int, int> d1 = new Dictionary<int, int>();
public void falling_disks(int[] A, int[] B)
{
var itemstoremove = new List<int>();
var en = d1.GetEnumerator();
int count = 0;
for (int i = 0; i <= A.Length - 1; i++)
{
d1.Add(count++, A[i]);
}
//for each incoming element in array
foreach (int ele in B)
{
//store prev as current position of enumerator
var prev = new KeyValuePair<int, int>();
prev = en.Current;
//check if it is possible to iterate to next element in dictionary
if (en.MoveNext())
{
//loop till end of dictionary
while (en.MoveNext())
{
//if current value of enumerator in dictionary is less than incoming element and check if corroesponding key for that value is in hashtable or not
if (en.Current.Value <= ele && !(checkifthatvalueisfilled(en.Current.Key)))
continue;
else
{//if current enumerator value is greater than incoming element from array B then remove all elements from prev reference till end of dictionary
h1.Add(en.Current.Key, true);
listofitemstoremove.Add(en.Current.Key);
}
prev = en.Current;
}
if (!(h1.ContainsKey(en.Current.Key)))
{
h1.Add(en.Current.Key, true);
listofitemstoremove.Add(en.Current.Key);
}
}
else
{
h1.Add(prev.Key, true);
listofitemstoremove.Add(prev.Key);
}
foreach (int item in listofitemstoremove)
{
for (int i = item; i < d1.Count; i++)
{
d1.Remove(i++);
}
}
}
Console.WriteLine(h1.Count);
}
public bool checkifthatvalueisfilled(int value)
{
if (h1.ContainsValue(h1.ContainsKey(value)) == true)
return true;
else return false;
}
}
class Program
{
static void Main(string[] args)
{
int[] A = new int[] { 5, 6, 4, 3, 6, 2, 3 };
int[] B = new int[] { 2, 3 };
Solution s1 = new Solution();
s1.falling_disks(A, B);
}
}
}

It seems like you want to be able to access the previous value as well as the current value of the sequence. Here is a simple helper method that takes a sequence and turns it into a sequence of pairs that represents each value with it's previous value in the original sequence:
public static IEnumerable<Tuple<T, T>> GroupAdjacent<T>(IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
T previous = iterator.Current;
while (iterator.MoveNext())
{
yield return Tuple.Create(previous, iterator.Current);
}
}
}
It could then be used like:
foreach(var pair in GroupAdjacent(dictionary))
{
var previous = pair.Item1;
var current = pair.Item2;
}

Is there a good reason you cannot use:-
// Replace TKey and TValue with the types from the dictionary
TKey previousKey;
TValue previousValue;
bool first = true;
foreach(var key in dictionary.Keys)
{
var value = dictionary[key];
if (!first)
{
... // Do whatever you need to do with the keys and values
}
previousKey = key;
previousValue = value;
first = false;
}
(Note, though, that you'll likely have to .OrderBy(...) your .Keys for this to make any sense)

List<KeyValuePair<Int32, Int32>> keyValuePairsWithIterations = new List<KeyValuePair<Int32, Int32>>();
foreach(var keyvaluepair in d1)
{
keyValuePairsWithIterations.Add(keyvaluepair);
}
Now you can access your keyvaluepairs by iteration. But it look a little bit strengh... And I still dont understand for what you need it...

As well as Iain's and the other's approaches, you can also do it like this (like Iain's, this gives previous and current rather than current and next, but they are pretty much the same thing):
using System;
using System.Collections.Generic;
namespace Demo
{
public static class Program
{
private static void Main(string[] args)
{
var d1 = new Dictionary<int, int> {{1, 1}, {2, 2}, {3, 3}, {4, 4}};
bool isFirst = true;
var previous = new KeyValuePair<int, int>();
foreach (var current in d1)
{
if (!isFirst)
{
// You have current and previous available now.
Console.WriteLine("Current = " + current.Value + ", previous = " + previous.Value);
}
previous = current;
isFirst = false;
}
}
}
}
And here's how you can do it by manually using the enumerator:
using System;
using System.Collections.Generic;
namespace Demo
{
public static class Program
{
private static void Main(string[] args)
{
var d1 = new Dictionary<int, int> {{1, 1}, {2, 2}, {3, 3}, {4, 4}};
var iter = d1.GetEnumerator();
if (iter.MoveNext())
{
var previous = iter.Current;
while (iter.MoveNext())
{
// You have current and previous available now.
Console.WriteLine("Current = " + iter.Current.Value + ", previous = " + previous.Value);
previous = iter.Current;
}
}
}
}
}

Why do you need an iterator? The foreach loop will do that for you.
If you need the current and previous item, you could just store the previous item in every iteration:
Dictionary<int, int> d1 = new Dictionary<int, int>();
KeyValuePair<int, int> previous = null;
KeyValuePair<int, int> current = null;
foreach (KeyValuePair<int, int> item in d1)
{
previous = current;
current = item;
// do what you need to do with previous and current
}
You could also use a SortedDictionary, that will give you an index.

Related

algorithm for selecting N random elements from a List<T> in C# [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 6 years ago.
I need a quick algorithm to select 4 random elements from a generic list. For example, I'd like to get 4 random elements from a List and then based on some calculations if elements found not valid then it should again select next 4 random elements from the list.
You could do it like this
public static class Extensions
{
public static Dictionary<int, T> GetRandomElements<T>(this IList<T> list, int quantity)
{
var result = new Dictionary<int, T>();
if (list == null)
return result;
Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < quantity; i++)
{
int idx = rnd.Next(0, list.Count);
result.Add(idx, list[idx]);
}
return result;
}
}
Then use the extension method like this:
List<string> list = new List<string>() { "a", "b", "c", "d", "e", "f", "g", "h" };
Dictionary<int, string> randomElements = list.GetRandomElements(3);
foreach (KeyValuePair<int, string> elem in randomElements)
{
Console.WriteLine($"index in original list: {elem.Key} value: {elem.Value}");
}
something like that:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
int n = 4;
var rand = new Random();
var randomObjects = new List<int>();
for (int i = 0; i<n; i++)
{
var index = rand.Next(list.Count);
randomObjects.Add(list[index]);
}
}
}
You can store indexes in some list to get non-repeated indexes:
List<T> GetRandomElements<T>(List<T> allElements, int randomCount = 4)
{
if (allElements.Count < randomCount)
{
return allElements;
}
List<int> indexes = new List<int>();
// use HashSet if performance is very critical and you need a lot of indexes
//HashSet<int> indexes = new HashSet<int>();
List<T> elements = new List<T>();
Random random = new Random();
while (indexes.Count < randomCount)
{
int index = random.Next(allElements.Count);
if (!indexes.Contains(index))
{
indexes.Add(index);
elements.Add(allElements[index]);
}
}
return elements;
}
Then you can do some calculation and call this method:
void Main(String[] args)
{
do
{
List<int> elements = GetRandomelements(yourElements);
//do some calculations
} while (some condition); // while result is not right
}
Suppose that the length of the List is N. Now suppose that you will put these 4 numbers in another List called out. Then you can loop through the List and the probability of the element you are on being chosen is
(4 - (out.Count)) / (N - currentIndex)
funcion (list)
(
loop i=0 i < 4
index = (int) length(list)*random(0 -> 1)
element[i] = list[index]
return element
)
while(check == false)
(
elements = funcion (list)
Do some calculation which returns check == false /true
)
This is the pseudo code, but i think you should of come up with this yourself.
Hope it helps:)
All the answers up to now have one fundamental flaw; you are asking for an algorithm that will generate a random combination of n elements and this combination, following some logic rules, will be valid or not. If its not, a new combination should be produced. Obviously, this new combination should be one that has never been produced before. All the proposed algorithms do not enforce this. If for example out of 1000000 possible combinations, only one is valid, you might waste a whole lot of resources until that particular unique combination is produced.
So, how to solve this? Well, the answer is simple, create all possible unique solutions, and then simply produce them in a random order. Caveat: I will suppose that the input stream has no repeating elements, if it does, then some combinations will not be unique.
First of all, lets write ourselves a handy immutable stack:
class ImmutableStack<T> : IEnumerable<T>
{
public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
private readonly T head;
private readonly ImmutableStack<T> tail;
public int Count { get; }
private ImmutableStack()
{
Count = 0;
}
private ImmutableStack(T head, ImmutableStack<T> tail)
{
this.head = head;
this.tail = tail;
Count = tail.Count + 1;
}
public T Peek()
{
if (this == Empty)
throw new InvalidOperationException("Can not peek a empty stack.");
return head;
}
public ImmutableStack<T> Pop()
{
if (this == Empty)
throw new InvalidOperationException("Can not pop a empty stack.");
return tail;
}
public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);
public IEnumerator<T> GetEnumerator()
{
var current = this;
while (current != Empty)
{
yield return current.head;
current = current.tail;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
This will make our life easier while producing all combinations by recursion. Next, let's get the signature of our main method right:
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinationsInRandomOrder<T>(
IEnumerable<T> data, int combinationLength)
Ok, that looks about right. Now let's implement this thing:
var allCombinations = GetAllPossibleCombinations(data, combinationLength).ToArray();
var rnd = new Random();
var producedIndexes = new HashSet<int>();
while (producedIndexes.Count < allCombinations.Length)
{
while (true)
{
var index = rnd.Next(allCombinations.Length);
if (!producedIndexes.Contains(index))
{
producedIndexes.Add(index);
yield return allCombinations[index];
break;
}
}
}
Ok, all we are doing here is producing random indexees, checking we haven't produced it yet (we use a HashSet<int> for this), and returning the combination at that index.
Simple, now we only need to take care of GetAllPossibleCombinations(data, combinationLength).
Thats easy, we'll use recursion. Our bail out condition is when our current combination is the specified length. Another caveat: I'm omitting argument validation throughout the whole code, things like checking for null or if the specified length is not bigger than the input length, etc. should be taken care of.
Just for the fun, I'll be using some minor C#7 syntax here: nested functions.
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinations<T>(
IEnumerable<T> stream, int length)
{
return getAllCombinations(stream, ImmutableStack<T>.Empty);
IEnumerable<IEnumerable<T>> getAllCombinations<T>(IEnumerable<T> currentData, ImmutableStack<T> combination)
{
if (combination.Count == length)
yield return combination;
foreach (var d in currentData)
{
var newCombination = combination.Push(d);
foreach (var c in getAllCombinations(currentData.Except(new[] { d }), newCombination))
{
yield return c;
}
}
}
}
And there we go, now we can use this:
var data = "abc";
var random = GetAllPossibleCombinationsInRandomOrder(data, 2);
foreach (var r in random)
{
Console.WriteLine(string.Join("", r));
}
And sure enough, the output is:
bc
cb
ab
ac
ba
ca

Create a list of all permutation from different lists [duplicate]

This one should not be too hard but my mind seems to be having a stack overflow (huehue). I have a series of Lists and I want to find all permutations they can be ordered in. All of the lists have different lengths.
For example:
List 1: 1
List 2: 1, 2
All permutations would be:
1, 1
1, 2
In my case I don't switch the numbers around. (For example 2, 1)
What is the easiest way to write this?
I can't say if the following is the easiest way, but IMO it's the most efficient way. It's basically a generalized version of the my answer to the Looking at each combination in jagged array:
public static class Algorithms
{
public static IEnumerable<T[]> GenerateCombinations<T>(this IReadOnlyList<IReadOnlyList<T>> input)
{
var result = new T[input.Count];
var indices = new int[input.Count];
for (int pos = 0, index = 0; ;)
{
for (; pos < result.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return result;
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Count);
}
}
}
You can see the explanation in the linked answer (shortly it's emulating nested loops). Also since for performace reasons it yields the internal buffer w/o cloning it, you need to clone it if you want store the result for later processing.
Sample usage:
var list1 = new List<int> { 1 };
var list2 = new List<int> { 1, 2 };
var lists = new[] { list1, list2 };
// Non caching usage
foreach (var combination in lists.GenerateCombinations())
{
// do something with the combination
}
// Caching usage
var combinations = lists.GenerateCombinations().Select(c => c.ToList()).ToList();
UPDATE: The GenerateCombinations is a standard C# iterator method, and the implementation basically emulates N nested loops (where N is the input.Count) like this (in pseudo code):
for (int i0 = 0; i0 < input[0].Count; i0++)
for (int i1 = 0; i1 < input[1].Count; i1++)
for (int i2 = 0; i2 < input[2].Count; i2++)
...
for (int iN-1 = 0; iN-1 < input[N-1].Count; iN-1++)
yield { input[0][i0], input[1][i1], input[2][i2], ..., input[N-1][iN-1] }
or showing it differently:
for (indices[0] = 0; indices[0] < input[0].Count; indices[0]++)
{
result[0] = input[0][indices[0]];
for (indices[1] = 0; indices[1] < input[1].Count; indices[1]++)
{
result[1] = input[1][indices[1]];
// ...
for (indices[N-1] = 0; indices[N-1] < input[N-1].Count; indices[N-1]++)
{
result[N-1] = input[N-1][indices[N-1]];
yield return result;
}
}
}
Nested loops:
List<int> listA = (whatever), listB = (whatever);
var answers = new List<Tuple<int,int>>;
for(int a in listA)
for(int b in listB)
answers.add(Tuple.create(a,b));
// do whatever with answers
Try this:
Func<IEnumerable<string>, IEnumerable<string>> combine = null;
combine = xs =>
xs.Skip(1).Any()
? xs.First().SelectMany(x => combine(xs.Skip(1)), (x, y) => String.Format("{0}{1}", x, y))
: xs.First().Select(x => x.ToString());
var strings = new [] { "AB", "12", "$%" };
foreach (var x in combine(strings))
{
Console.WriteLine(x);
}
That gives me:
A1$
A1%
A2$
A2%
B1$
B1%
B2$
B2%
I made the following IEnumerable<IEnumerable<TValue>> class to solve this problem which allows use of generic IEnumerable's and whose enumerator returns all permutations of the values, one from each inner list. It can be conventiently used directly in a foreach loop.
It's a variant of Michael Liu's answer to IEnumerable and Recursion using yield return
I've modified it to return lists with the permutations instead of the single values.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Permutation
{
public class ListOfListsPermuter<TValue> : IEnumerable<IEnumerable<TValue>>
{
private int count;
private IEnumerable<TValue>[] listOfLists;
public ListOfListsPermuter(IEnumerable<IEnumerable<TValue>> listOfLists_)
{
if (object.ReferenceEquals(listOfLists_, null))
{
throw new ArgumentNullException(nameof(listOfLists_));
}
listOfLists =listOfLists_.ToArray();
count = listOfLists.Count();
for (int i = 0; i < count; i++)
{
if (object.ReferenceEquals(listOfLists[i], null))
{
throw new NullReferenceException(string.Format("{0}[{1}] is null.", nameof(listOfLists_), i));
}
}
}
// A variant of Michael Liu's answer in StackOverflow
// https://stackoverflow.com/questions/2055927/ienumerable-and-recursion-using-yield-return
public IEnumerator<IEnumerable<TValue>> GetEnumerator()
{
TValue[] currentList = new TValue[count];
int level = 0;
var enumerators = new Stack<IEnumerator<TValue>>();
IEnumerator<TValue> enumerator = listOfLists[level].GetEnumerator();
try
{
while (true)
{
if (enumerator.MoveNext())
{
currentList[level] = enumerator.Current;
level++;
if (level >= count)
{
level--;
yield return currentList;
}
else
{
enumerators.Push(enumerator);
enumerator = listOfLists[level].GetEnumerator();
}
}
else
{
if (level == 0)
{
yield break;
}
else
{
enumerator.Dispose();
enumerator = enumerators.Pop();
level--;
}
}
}
}
finally
{
// Clean up in case of an exception.
enumerator?.Dispose();
while (enumerators.Count > 0)
{
enumerator = enumerators.Pop();
enumerator.Dispose();
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
You can use it directly in a foreach like this:
public static void Main(string[] args)
{
var listOfLists = new List<List<string>>()
{
{ new List<string>() { "A", "B" } },
{ new List<string>() { "C", "D" } }
};
var permuter = new ListOfListsPermuter<string>(listOfLists);
foreach (IEnumerable<string> item in permuter)
{
Console.WriteLine("{ \"" + string.Join("\", \"", item) + "\" }");
}
}
The output:
{ "A", "C" }
{ "A", "D" }
{ "B", "C" }
{ "B", "D" }

Query for values

Declarations and entries in two_Dict dictionary is created as given:
Dictionary<string, List<string>>two_Dict = new Dictionary<string, List<string>>();
List<string> list;
if (!two_Dict.TryGetValue(d.ToString(), out list))
{
two_Dict.Add( d.ToString(), list = new List<string>());
list.Add(possibility_cell_list[0]);
list.Add(possibility_cell_list[1]);
}
Sample entries in two_Dict:
two_Dict["5"] Count = 2 [0]: "A2" [1]: "D2"
two_Dict["6"] Count = 2 [0]: "A2" [1]: "D2"
I am looking to form a linq query to get the keys which have the same list entries in the dictionary two_Dict. Any help will be appreciated.
You can use a fairly simple expression with linq:
var keys = from kvp1 in two_dict
where two_dict.Any(kvp2 => kvp2.Key != kvp1.Key
&& kvp2.Value.SequenceEqual(kvp1.Value))
select kvp1.Key;
However, this does not give the best performance as it will search through the entire dictionary n times, where n is the number of entries in the dictionary.
You can get slightly better performance if you only look at the items that have already been looked at so far. This way, on average you only go through half of the dictionary n times, so it's theoretically twice as fast. Unfortunately, I don't think there is a good way to do this purely using linq.
public static IEnumerable GetDuplicates(IDictionary<string, List<string>> dict)
{
var previousItems = new List<KeyValuePair<string, List<string>>>(dict.Count);
var matchedItems = new List<bool>();
foreach (var kvp in dict)
{
var match = previousItems.Select((kvp2, i) => Tuple.Create(kvp2.Key, kvp2.Value, i)).FirstOrDefault(t => kvp.Value.SequenceEqual(t.Item2));
if (match != null)
{
var index = match.Item3;
if (!matchedItems[index])
{
yield return match.Item1;
matchedItems[index] = true;
}
yield return kvp.Key;
}
else
{
previousItems.Add(kvp);
matchedItems.Add(false);
}
}
}
You would call the function like this:
var keys = GetDuplicates(two_dict);
All linq, no duplicate result but also, be careful, no null checks:
var res = from k1 in dict.Keys
from k2 in dict.Keys
where string.Compare(k1, k2) == -1 &&
dict[k1].Count == dict[k2].Count && !dict[k1].Any(x => !dict[k2].Contains(x)) && !dict[k2].Any(x => !dict[k1].Contains(x))
select new { k1, k2 };
How about this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, List<string>> two_Dict = new Dictionary<string, List<string>>();
var keyIndex = two_Dict.Keys.AsEnumerable()
.Select((x, i) => new { index = i, value = x })
.ToList();
var result = (from key1 in keyIndex
from key2 in keyIndex
where key1.index > key2.index
where AreEqual(two_Dict[key1.value],two_Dict[key2.value])
select new {key1 = key1.value, key2 = key2.value}).ToList();
}
static bool AreEqual<T>(List<T> x, List<T> y)
{
// same list or both are null
if (x == y)
{
return true;
}
// one is null (but not the other)
if (x == null || y == null)
{
return false;
}
// count differs; they are not equal
if (x.Count != y.Count)
{
return false;
}
x.Sort();
y.Sort();
for (int i = 0; i < x.Count; i++)
{
if (!x[i].Equals(y[i]))
{
return false;
}
}
return true;
}
}
}
​

Grouping by an unknown initial prefix

Say I have the following array of strings as an input:
foo-139875913
foo-aeuefhaiu
foo-95hw9ghes
barbazabejgoiagjaegioea
barbaz8gs98ghsgh9es8h
9a8efa098fea0
barbaza98fyae9fghaefag
bazfa90eufa0e9u
bazgeajga8ugae89u
bazguea9guae
aifeaufhiuafhe
There are 3 different prefixes used here, "foo-", "barbaz" and "baz" - however these prefixes are not known ahead of time (they could be something completely different).
How could you establish what the different common prefixes are so that they could then be grouped by? This is made a bit tricky since in the data I've provided there's two that start with "bazg" and one that starts "bazf" where of course "baz" is the prefix.
What I've tried so far is sorting them into alphabetical order, and then looping through them in order and counting how many characters in a row are identical to the previous. If the number is different or when 0 characters are identical, it starts a new group. The problem with this is it falls over at the "bazg" and "bazf" problem I mentioned earlier and separates those into two different groups (one with just one element in it)
Edit: Alright, let's throw a few more rules in:
Longer potential groups should generally be preferred over shorter ones, unless there is a closely matching group of less than X characters difference in length. (So where X is 2, baz would be preferred over bazg)
A group must have at least Y elements in it or not be a group at all
It's okay to simply throw away elements that don't match any of the 'groups' to within the rules above.
To clarify the first rule in relation to the second, if X was 0 and Y was 2, then the two 'bazg' entries would be in a group, and the 'bazf' would be thrown away because its on its own.
Well, here's a quick hack, probably O(something_bad):
IEnumerable<Tuple<String, IEnumerable<string>>> GuessGroups(IEnumerable<string> source, int minNameLength=0, int minGroupSize=1)
{
// TODO: error checking
return InnerGuessGroups(new Stack<string>(source.OrderByDescending(x => x)), minNameLength, minGroupSize);
}
IEnumerable<Tuple<String, IEnumerable<string>>> InnerGuessGroups(Stack<string> source, int minNameLength, int minGroupSize)
{
if(source.Any())
{
var tuple = ExtractTuple(GetBestGroup(source, minNameLength), source);
if (tuple.Item2.Count() >= minGroupSize)
yield return tuple;
foreach (var element in GuessGroups(source, minNameLength, minGroupSize))
yield return element;
}
}
Tuple<String, IEnumerable<string>> ExtractTuple(string prefix, Stack<string> source)
{
return Tuple.Create(prefix, PopWithPrefix(prefix, source).ToList().AsEnumerable());
}
IEnumerable<string> PopWithPrefix(string prefix, Stack<string> source)
{
while (source.Any() && source.Peek().StartsWith(prefix))
yield return source.Pop();
}
string GetBestGroup(IEnumerable<string> source, int minNameLength)
{
var s = new Stack<string>(source);
var counter = new DictionaryWithDefault<string, int>(0);
while(s.Any())
{
var g = GetCommonPrefix(s);
if(!string.IsNullOrEmpty(g) && g.Length >= minNameLength)
counter[g]++;
s.Pop();
}
return counter.OrderBy(c => c.Value).Last().Key;
}
string GetCommonPrefix(IEnumerable<string> coll)
{
return (from len in Enumerable.Range(0, coll.Min(s => s.Length)).Reverse()
let possibleMatch = coll.First().Substring(0, len)
where coll.All(f => f.StartsWith(possibleMatch))
select possibleMatch).FirstOrDefault();
}
public class DictionaryWithDefault<TKey, TValue> : Dictionary<TKey, TValue>
{
TValue _default;
public TValue DefaultValue {
get { return _default; }
set { _default = value; }
}
public DictionaryWithDefault() : base() { }
public DictionaryWithDefault(TValue defaultValue) : base() {
_default = defaultValue;
}
public new TValue this[TKey key]
{
get { return base.ContainsKey(key) ? base[key] : _default; }
set { base[key] = value; }
}
}
Example usage:
string[] input = {
"foo-139875913",
"foo-aeuefhaiu",
"foo-95hw9ghes",
"barbazabejgoiagjaegioea",
"barbaz8gs98ghsgh9es8h",
"barbaza98fyae9fghaefag",
"bazfa90eufa0e9u",
"bazgeajga8ugae89u",
"bazguea9guae",
"9a8efa098fea0",
"aifeaufhiuafhe"
};
GuessGroups(input, 3, 2).Dump();
Ok, well as discussed, the problem wasn't initially well defined, but here is how I'd go about it.
Create a tree T
Parse the list, for each element:
for each letter in that element
if a branch labeled with that letter exists then
Increment the counter on that branch
Descend that branch
else
Create a branch labelled with that letter
Set its counter to 1
Descend that branch
This gives you a tree where each of the leaves represents a word in your input. Each of the non-leaf nodes has a counter representing how many leaves are (eventually) attached to that node. Now you need a formula to weight the length of the prefix (the depth of the node) against the size of the prefix group. For now:
S = (a * d) + (b * q) // d = depth, q = quantity, a, b coefficients you'll tweak to get desired behaviour
So now you can iterate over each of the non-leaf node and assign them a score S. Then, to work out your groups you would
For each non-leaf node
Assign score S
Insertion sort the node in to a list, so the head is the highest scoring node
Starting at the root of the tree, traverse the nodes
If the node is the highest scoring node in the list
Mark it as a prefix
Remove all nodes from the list that are a descendant of it
Pop itself off the front of the list
Return up the tree
This should give you a list of prefixes. The last part feels like some clever data structures or algorithms could speed it up (the last part of removing all the children feels particularly weak, but if you input size is small, I guess speed isn't too important).
I'm wondering if your requirements aren't off. It seems as if you are looking for a specific grouping size as opposed to specific key size requirements. I have below a program that will, based on a specified group size, break up the strings into the largest possible groups up too, and including the group size specified. So if you specify a group size of 5, then it will group items on the smallest key possible to make a group of size 5. In your example it would group foo- as f since there is no need to make a more complex key as an identifier.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
/// <remarks><c>true</c> in returned dictionary key are groups over <paramref name="maxGroupSize"/></remarks>
public static Dictionary<bool,Dictionary<string, List<string>>> Split(int maxGroupSize, int keySize, IEnumerable<string> items)
{
var smallItems = from item in items
where item.Length < keySize
select item;
var largeItems = from item in items
where keySize < item.Length
select item;
var largeItemsq = (from item in largeItems
let key = item.Substring(0, keySize)
group item by key into x
select new { Key = x.Key, Items = x.ToList() } into aGrouping
group aGrouping by aGrouping.Items.Count() > maxGroupSize into x2
select x2).ToDictionary(a => a.Key, a => a.ToDictionary(a_ => a_.Key, a_ => a_.Items));
if (smallItems.Any())
{
var smallestLength = items.Aggregate(int.MaxValue, (acc, item) => Math.Min(acc, item.Length));
var smallItemsq = (from item in smallItems
let key = item.Substring(0, smallestLength)
group item by key into x
select new { Key = x.Key, Items = x.ToList() } into aGrouping
group aGrouping by aGrouping.Items.Count() > maxGroupSize into x2
select x2).ToDictionary(a => a.Key, a => a.ToDictionary(a_ => a_.Key, a_ => a_.Items));
return Combine(smallItemsq, largeItemsq);
}
return largeItemsq;
}
static Dictionary<bool, Dictionary<string,List<string>>> Combine(Dictionary<bool, Dictionary<string,List<string>>> a, Dictionary<bool, Dictionary<string,List<string>>> b) {
var x = new Dictionary<bool,Dictionary<string,List<string>>> {
{ true, null },
{ false, null }
};
foreach(var condition in new bool[] { true, false }) {
var hasA = a.ContainsKey(condition);
var hasB = b.ContainsKey(condition);
x[condition] = hasA && hasB ? a[condition].Concat(b[condition]).ToDictionary(c => c.Key, c => c.Value)
: hasA ? a[condition]
: hasB ? b[condition]
: new Dictionary<string, List<string>>();
}
return x;
}
public static Dictionary<string, List<string>> Group(int maxGroupSize, IEnumerable<string> items, int keySize)
{
var toReturn = new Dictionary<string, List<string>>();
var both = Split(maxGroupSize, keySize, items);
if (both.ContainsKey(false))
foreach (var key in both[false].Keys)
toReturn.Add(key, both[false][key]);
if (both.ContainsKey(true))
{
var keySize_ = keySize + 1;
var xs = from needsFix in both[true]
select needsFix;
foreach (var x in xs)
{
var fixedGroup = Group(maxGroupSize, x.Value, keySize_);
toReturn = toReturn.Concat(fixedGroup).ToDictionary(a => a.Key, a => a.Value);
}
}
return toReturn;
}
static Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
const string allowedChars = "aaabbbbccccc"; // "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
static readonly int maxAllowed = allowedChars.Length - 1;
static IEnumerable<string> GenerateText()
{
var list = new List<string>();
for (int i = 0; i < 100; i++)
{
var stringLength = rand.Next(3,25);
var chars = new List<char>(stringLength);
for (int j = stringLength; j > 0; j--)
chars.Add(allowedChars[rand.Next(0, maxAllowed)]);
var newString = chars.Aggregate(new StringBuilder(), (acc, item) => acc.Append(item)).ToString();
list.Add(newString);
}
return list;
}
static void Main(string[] args)
{
// runs 1000 times over autogenerated groups of sample text.
for (int i = 0; i < 1000; i++)
{
var s = GenerateText();
Go(s);
}
Console.WriteLine();
Console.WriteLine("DONE");
Console.ReadLine();
}
static void Go(IEnumerable<string> items)
{
var dict = Group(3, items, 1);
foreach (var key in dict.Keys)
{
Console.WriteLine(key);
foreach (var item in dict[key])
Console.WriteLine("\t{0}", item);
}
}
}
}

How can I access the next value in a collection inside a foreach loop in C#?

I'm working in C# and with a sorted List<T> of structs. I'm trying to iterate through the List and for each iteration I'd like to access the next member of the list. Is there a way to do this?
Pseudocode example:
foreach (Member member in List)
{
Compare(member, member.next);
}
You can't. Use a for instead
for(int i=0; i<list.Count-1; i++)
Compare(list[i], list[i+1]);
You could just keep the previous value instead:
T prev = default(T);
bool first = true;
foreach(T item in list) {
if(first) {
first = false;
} else {
Compare(prev, item);
}
prev = item;
}
If one were so inclined, you could probably write an Extension method for this as well...
public static void ForEachNext<T>(this IList<T> collection, Action<T, T> func)
{
for (int i = 0; i < collection.Count - 1; i++)
func(collection[i], collection[i + 1]);
}
Usage:
List<int> numList = new List<int> { 1, 3, 5, 7, 9, 11, 13, 15 };
numList.ForEachNext((first, second) =>
{
Console.WriteLine(string.Format("{0}, {1}", first, second));
});
Use a regular for loop with an index, and compare list[i] and list[i+1]. (But make sure to only loop until the second-to-last index.)
Or, if you really want to use a foreach, you can keep a Member reference to the previous member and check the next time around. But I wouldn't recommend it.
LINQ might be your friend here. This approach will work with anything that's IEnumerable<T>, not just IList<T> collections, which is very useful if your collection never ends or is otherwise calculated on-the-fly:
class Program {
static void Main(string[] args) {
var list = new List<Int32> { 1, 2, 3, 4, 5 };
foreach (var comparison in list.Zip(list.Skip(1), Compare)) {
Console.WriteLine(comparison);
}
Console.ReadKey();
}
static Int32 Compare(Int32 first, Int32 second) {
return first - second;
}
}
XmlNode root = xdoc.DocumentElement;
XmlNodeList nodeList = root.SelectNodes("descendant::create-backup-sets/new-file-system-backup-set");
for (int j = 0; j < nodeList.Count; j++ )
{
for (int i = 0; i <= nodeList.Item(j).ChildNodes.Count - 1; i++)
{
if (nodeList.Item(j).ChildNodes[i].Name == "basic-set-info")
{
if (nodeList.Item(j).ChildNodes[i].Attributes["name"].Value != null)
{
// retrieve backup name
_bName = nodeList.Item(j).ChildNodes[i].Attributes["name"].Value.ToString();
}
}
You can do it by IndexOf but FYI IndexOf get the first item equal to the item so if you have a lot of items with the same value it's better to use for loop instead or define the range of search. Official docs are here
foreach (Member member in List)
{
var nextItem = List[List.IndexOf(member)+1];
Compare(member, nextItem);
}

Categories