nested hashset of lists? - c#

I'm working on one of the project Euler problems, and I wanted to take the approach of creating a list of values, and adding the list to a Hashset, this way I could evaluate in constant time if the list already exists in the hashset, with the end goal to count the number of lists in the hashset for my end result.
The problem I'm having is when I create a list in this manner.
HashSet<List<int>> finalList = new HashSet<List<int>>();
List<int> candidate = new List<int>();
candidate.Add(5);
finalList.Add(candidate);
if (finalList.Contains(candidate) == false) finalList.Add(candidate);
candidate.Clear();
//try next value
Obviously the finalList[0] item is cleared when I clear the candidate and is not giving me the desired result. Is it possible to have a hashset of lists(of integers) like this? How would I ensure a new list is instantiated each time and added as a new item to the hashset, perhaps say in a for loop testing many values and possible list combinations?

Why don't you use a value which is unique for each list as a key or identifier? You could create a HashSet for your keys which will unlock your lists.

You can use a Dictionary instead. The only thing is you have to test to see if the Dictionary already has the list. This is easy to do, by creating a simple class that supports this need.
class TheSimpleListManager
{
private Dictionary<String, List<Int32>> Lists = new Dictionary<String, List<Int32>>();
public void AddList(String key, List<Int32> list)
{
if(!Lists.ContainsKey(key))
{
Lists.Add(key, list);
}
else
{
// list already exists....
}
}
}
This is just a quick sample of an approach.

To fix your clear issue: Since its an object reference, you would have to create a new List and add it to the HashSet.
You can create the new List by passing the old one into its constructor.
HashSet<List<int>> finalList = new HashSet<List<int>>();
List<int> candidate = new List<int>();
candidate.Add(5);
var newList = new List<int>(candidate);
finalList.Add(newList);
if (finalList.Contains(newList) == false) //Not required for HashSet
finalList.Add(newList);
candidate.Clear();
NOTE: HashSet internally does a contains before adding items. In otherwords, here even if you execute finalList.Add(newList); n times, it would add newList only once. Therefore it is not necessary to do a contains check.

Related

Inserting the first value of a key-value pair, when the value is a list

I have a dictionary as below, where the key is a string and the value is a list of doubles:
Dictionary<string, List<double>> dataStore = new Dictionary<string, List<double>>();
List<string> channel_names = new List<string>(); // contains the keys
Now when I want to add data to this dictionary, I do:
if (dataStore.ContainsKey(channel_names[j]))
{
dataStore[channel_names[j]].Add(measurement);
}
else
{
dataStore.Add(channel_names[j], new List<double>((int)measurement));
}
The first statement (adding to an existing key) works fine, but something is wrong with the second statement, i.e. when I am trying to initialise the keys with the first double in the list. The first measurement is being missed out. Can anyone please advise as to why?
Thanks
You are using the constructor List(int), where int specifies the initial capacity of the list; it does not add that number to the list.
You could instead use collection-initialiser syntax:
new List<double> { measurement }
You can refactor your code to this solution:
if (!dataStore.ContainsKey(channel_names[j]))
{
dataStore.Add(channel_names[j], new List<double>());
}
dataStore[channel_names[j]].Add(measurement);
It will make it clearer that the measurment is always added to the list, regardless of the result of the ContainsKey method.
There are two constructors for List<T> that do different things.
When you call new List<T>(int) you create a new list of the provided size - or more exact the initial capacity of that list.
When you want to create and fill a new list in one go, you should use a collection-initializer:
dataStore.Add(channel_names[j], new List<double> { measurement });
As an asside you can simplify your code:
if (!dataStore.ContainsKey(channel_names[j]))
{
dataStore[channel_names[j]] = new List<int>();
}
dataStore[channel_names[j]].Add(measurement);
As I mention in the comments and others have mentioned, you aren't adding things to the list when you construct it, you are allocating capacity to the list. The code you want is:
if (!dataStore.ContainsKey(channel_names[j]))
{
dataStore.Add(channel_names[j], new List<double>();
}
dataStore[channel_names[j]].Add(measurement);
If the Dictionary doesn't contain the key you want, you create a new list and add it to the dictionary. At this point, the dictionary necessarily contains the appropriate key (it either already existed and you skipped the if block, or it didn't exist and you created it). In any case, at that point, you can just add the measurement to the appropriate list.
You have two alternatives:
If you have list for given key, just add measurement to the existing value (which is list)
If dataStore doesn't have the key, you should add entire key-value pair: key (channel_names[j]) and value which is list with one item
Code:
if (dataStore.TryGetValue(channel_names[j], out var list))
// the key exists; just add measurement to the existing list
list.Add(measurement);
else
// no key found; add key-value pair: key and a list with one item
dataStore.Add(channel_names[j], new List<double>() {measurement});
The constructor on List<T> which takes an integer is not adding that integer to the list. It is setting the capacity of the list.
Instead you could use an initializer...
dataStore.Add(channel_names[j], new List<double>() { measurement };
Alternatively, you could use Dictionary.TryGetValue to find the List<double> for the given string, add it if it doesn't exist, then just modify it.
List<double> values = null;
if (!dataStore.TryGetValue(channel_names[j], out values))
{
values = new List<double>();
dataStore.Add(channel_names[j], values);
}
values.Add(measurement);

Sort Dictionary based on values in a list of integers

I'm having a problem sorting a dictionary based on the sum of 1s in lists of integers inside the same Dictionary. So first I want to count the 1s in each list and then sort the dictionary based on the result.
I've found some solutions in Stackoverflow but they don't answer my question.
Th dictionary looks like the following:
Dictionary<int, List<int>> myDic = new Dictionary<int, List<int>>();
List<int> myList = new List<int>();
myList = new List<int>();//Should appear third
myList.Add(0);
myList.Add(0);
myList.Add(1);
myDic.Add(0, myList);
myList = new List<int>();//Should appear second
myList.Add(1);
myList.Add(1);
myList.Add(0);
myDic.Add(1, myList);
myList = new List<int>();//Should appear first
myList.Add(1);
myList.Add(1);
myList.Add(1);
myDic.Add(2, myList);
I tried this code but it seems it doesn't do anything.
List<KeyValuePair<int, List<int>>> myList2 = myDic.ToList();
myList2.Sort((firstPair, nextPair) =>
{
return firstPair.Value.Where(i=>i==1).Sum().CompareTo(nextPair.Value.Where(x=>x==1).Sum());
});
You are sorting list items in ascending order. I.e. items with more 1s will go to the end of list. You should use descending order. Just compare nextPair to firstPair (or change sign of comparison result):
myList2.Sort((firstPair, nextPair) =>
{
return nextPair.Value.Where(i => i==1).Sum().CompareTo(
firstPair.Value.Where(x => x==1).Sum());
});
This approach has one problem - sum of 1s in value will be calculated each time two items are compared. Better use Enumerable.OrderByDescending. It's more simple to use, and it will compute comparison values (i.e. keys) only once. Thus Dictionary is a enumerable of KeyValuePairs, you can use OrderByDescending directly with dictionary:
var result = myDic.OrderByDescending(kvp => kvp.Value.Where(i => i == 1).Sum());
Your sort is backward, which is why you think it's not doing anything. Reverse the firstPair/nextPair values in your lambda and you'll get the result you expect.
Though, #Sergey Berezovskiy is correct, you could just use OrderBy, your example code could benefit from perhaps a different pattern overall.
class SummedKV
{
public KeyValuePair Kvp {get; set;}
public int Sum {get; set;}
}
var myList =
myDic.ToList()
.Select(kvp=> new SummedKV {Kvp = kvp, Sum = kvp.Value.Sum() });
myList.Sort(skv=>skv.Sum);
Maybe something simpler
myList2.OrderByDescending(x => x.Value.Sum());
Your code does do something. it creates a list of the items that used to be in the dictionary, sorted based on the number of 1 items contained in the list. The code that you have correctly creates this list and sorts it as your requirements say it should. (Note that using OrderByDescending would let you do the same thing more simply.)
It has no effect on the dictionary that you pulled the lists out of, of course. Dictionaries are unordered, so you can't "reorder" the items even if you wanted to. If it were some different type of ordered collection then it would be possible to change the order of it's items, but just creating a new structure and ordering that wouldn't do it; you'd need to use some sort of operation on the collection itself to change the order of the items.

Problems with Lists and Dictionaries

I'm having a problem with a Dictionary of Lists for both the Key and Value.
My dictionary is set up as this
Dictionary<List<string>,List<double>> f = new Dictionary<List<string>,List<double>>();
(it's like this for a very specific reason).
My problem is how to get the two lists out into their own lists. I have tried the following
List<string> s = new List<string>(f.Keys);
List<string> s = f.Select(kvp=>kvp.Keys).ToList()
List<string> s = f.Select(kvp=>kvp.Keys);
List<string> s = f.Keys;
as well as a variant using IEnumerable. No matter what I do, I can't seem to retrieve the Keys (or using f.Values, the values).
Any help here would be appreciated.
A list of strings seems like a VERY odd key for a dictionary, and will have complexities of its own, but you seem confident that it's correct, so I'll focus on your actual question.
Since Keys is a collection of key values, each of which is a List<string>, any of these should work:
List<List<string>> s = f.Select(kvp=>kvp.Key).ToList();
List<List<string>> s = f.Keys.ToList();
If you want ALL strings as a single list (essentially joining all of the lists together), you can use:
List<string> s2 = f.SelectMany(kvp => kvp.Key).ToList();
The SelectMany essentially selects each item from the collection within each key across the whole dictionary.
Lol This is probably the funniest thing I've seen in a while.
Alright. In c# there is a structure called KeyValuePair<TKey, TValue>. You can then iterate through the entire dataset with foreach and get access to what you want.
foreach(KeyValuePair<<List<string>,List<double>> item in f) {
List<string> key = item.key;
List<double> value = item.value;
}
If you have only 1 key,meaning 1 list of strings:
List<string> newf = f.Keys.ElementAt(0);
If you have more place another index.
Or check if the list as some item so that would be the list to retrieve:
List<string> newf = f.Keys.Single(k => k.Contains("SomeString"));
//this must exist or it will throw exception.
Get a key by checking if the corresponding values sum is above(or less,or equal...)
var newf1 = f.Where(k => k.Value.Sum() > 10).Select(v => v.Key);

How to keep values from a List<T> in a model after clearing the list?

I have a List that receives ids. It is instantiated outside the foreach statement.
List<int> indices = new List<int>();
foreach (var m in docsRelacionadosModel)
{
//.. do stuff
modelTemp.indices = indices;
//..here I do more stuff and some time it goes to the next iteration and I need to keep the value in indices to get more values.
//although in a condition
if(isOk) {
//I save the value of this list to a model
model.indices = modelTemp.indices;
//And I need to clear the list to get new values
indices.Clear(); <--- This will clear the values saved in model.indices
}
}
As it has values passed by reference, how can I keep the values in model.indices?
You need to make a copy of the list and save that copy to model.indecies. While there are a number of ways of copying the list the LINQ ToList extension method is probably the most convenient:
model.indices = modelTemp.indices.ToList();
Another option is to just use the List constructor:
model.indices = new List<int>(modelTemp.indices);
Just create a copy of the list:
model.indices = new List<int>(modelTemp.indices);
As per this S/O question, the easiest way may be to call ToList on your list:
model.indices = modelTemp.indices.ToList();
You could also instantiate as a new list, passing your list as a constructor parameter.

C# Sort List Based on Another List

I have a class that has multiple List<> contained within it. Its basically a table stored with each column as a List<>. Each column does not contain the same type. Each list is also the same length (has the same number of elements).
For example:
I have 3 List<> objects; one List, two List, and three List.
//Not syntactically correct
List<DateTime> one = new List...{4/12/2010, 4/9/2006, 4/13/2008};
List<double> two = new List...{24.5, 56.2, 47.4};
List<string> three = new List...{"B", "K", "Z"};
I want to be able to sort list one from oldest to newest:
one = {4/9/2006, 4/13/2008, 4/12/2010};
So to do this I moved element 0 to the end.
I then want to sort list two and three the same way; moving the first to the last.
So when I sort one list, I want the data in the corresponding index in the other lists to also change in accordance with how the one list is sorted.
I'm guessing I have to overload IComparer somehow, but I feel like there's a shortcut I haven't realized.
I've handled this design in the past by keeping or creating a separate index list. You first sort the index list, and then use it to sort (or just access) the other lists. You can do this by creating a custom IComparer for the index list. What you do inside that IComparer is to compare based on indexes into the key list. In other words, you are sorting the index list indirectly. Something like:
// This is the compare function for the separate *index* list.
int Compare (object x, object y)
{
KeyList[(int) x].CompareTo(KeyList[(int) y])
}
So you are sorting the index list based on the values in the key list. Then you can use that sorted key list to re-order the other lists. If this is unclear, I'll try to add a more complete example when I get in a situation to post one.
Here's a way to do it using LINQ and projections. The first query generates an array with the original indexes reordered by the datetime values; in your example, the newOrdering array would have members:
{ 4/9/2006, 1 }, { 4/13/2008, 2 }, { 4/12/2010, 0 }
The second set of statements generate new lists by picking items using the reordered indexes (in other words, items 1, 2, and 0, in that order).
var newOrdering = one
.Select((dateTime, index) => new { dateTime, index })
.OrderBy(item => item.dateTime)
.ToArray();
// now, order each list
one = newOrdering.Select(item => one[item.index]).ToList();
two = newOrdering.Select(item => two[item.index]).ToList();
three = newOrdering.Select(item => three[item.index]).ToList();
I am sorry to say, but this feels like a bad design. Especially because List<T> does not guarantee element order before you have called one of the sorting operations (so you have a problem when inserting):
From MSDN:
The List is not guaranteed to be
sorted. You must sort the List
before performing operations (such as
BinarySearch) that require the List
to be sorted.
In many cases you won't run into trouble based on this, but you might, and if you do, it could be a very hard bug to track down. For example, I think the current framework implementation of List<T> maintains insert order until sort is called, but it could change in the future.
I would seriously consider refactoring to use another data structure. If you still want to implement sorting based on this data structure, I would create a temporary object (maybe using an anonymous type), sort this, and re-create the lists (see this excellent answer for an explanation of how).
First you should create a Data object to hold everything.
private class Data
{
public DateTime DateTime { get; set; }
public int Int32 { get; set; }
public string String { get; set; }
}
Then you can sort like this.
var l = new List<Data>();
l.Sort(
(a, b) =>
{
var r = a.DateTime.CompareTo(b);
if (r == 0)
{
r = a.Int32.CompareTo(b);
if (r == 0)
{
r = a.String.CompareTo(b);
}
}
return r;
}
);
I wrote a sort algorithm that does this for Nito.LINQ (not yet released). It uses a simple-minded QuickSort to sort the lists, and keeps any number of related lists in sync. Source code starts here, in the IList<T>.Sort extension method.
Alternatively, if copying the data isn't a huge concern, you could project it into a LINQ query using the Zip operator (requires .NET 4.0 or Rx), order it, and then pull each result out:
List<DateTime> one = ...;
List<double> two = ...;
List<string> three = ...;
var combined = one.Zip(two, (first, second) => new { first, second })
.Zip(three, (pair, third) => new { pair.first, pair.second, third });
var ordered = combined.OrderBy(x => x.first);
var orderedOne = ordered.Select(x => x.first);
var orderedTwo = ordered.Select(x => x.second);
var orderedThree = ordered.Select(x => x.third);
Naturally, the best solution is to not separate related data in the first place.
Using generic arrays, this can get a bit cumbersome.
One alternative is using the Array.Sort() method that takes an array of keys and an array of values to sort. It first sorts the key array into ascending order and makes sure the array of values is reorganized to match this sort order.
If you're willing to incur the cost of converting your List<T>s to arrays (and then back), you could take advantage of this method.
Alternatively, you could use LINQ to combine the values from multiple arrays into a single anonymous type using Zip(), sort the list of anonymous types using the key field, and then split that apart into separate arrays.
If you want to do this in-place, you would have to write a custom comparer and create a separate index array to maintain the new ordering of items.
I hope this could help :
one = one.Sort(delegate(DateTime d1, DateTime d2)
{
return Convert.ToDateTime(d2).CompareTo(Convert.ToDateTime(d1));
});

Categories

Resources