Swapping elements between 2 hashsets c# - c#

If I have 2 hashsets of size 5, how can I take x items from the first hashset and swap them with x items from the second one ?
For example :
HashSet 1 has elements = {a , b, c , d ,e}
HashSet 2 has elements = {r , s ,t ,u , w}
After the swap I would like to obtain:
HashSet 1 = {r, s , c ,d e}
HashSet 2 = {a, b, t , u ,w}
I don't need a speficic order.

Perhaps:
HashSet<string> hash1 = new HashSet<string>() { "A1", "B1", "C1", "D1" };
HashSet<string> hash2 = new HashSet<string>() { "A2", "B2", "C2", "D2" };
var firstThreeInOne = hash1.Take(3).ToList();
var firstThreeInTwo = hash2.Take(3).ToList();
foreach (string str in firstThreeInOne)
hash1.Remove(str);
foreach (string str in firstThreeInTwo)
hash2.Remove(str);
foreach (string str in firstThreeInTwo)
hash1.Add(str);
foreach (string str in firstThreeInOne)
hash2.Add(str);
Test:
Console.WriteLine(string.Join(",", hash1)); // C2,B2,A2,D1
Console.WriteLine(string.Join(",", hash2)); // C1,B1,A1,D2
But note that a HashSet does not guarantee insertion order. It is simply not an ordered collection.
MSDN mentions that explicitly:
The HashSet<T> class provides high-performance set operations. A set
is a collection that contains no duplicate elements, and whose
elements are in no particular order.

You can't unless you will implement hashset with ordering.
HashSet class of CLR has not ordering, and you can't get "first N" elements

As DarkFalcon and others said the HashSet per definition has no order and therefor there are no first x elements in it.
BUT as with every collection you can get elements with the ElementAt method in some kind of order.
Be aware that you cannot know WHICH of the elements are counted as the first ones...
void Main()
{
var hashSet1 = new HashSet<int>();
hashSet1.Add(1);
hashSet1.Add(2);
hashSet1.Add(3);
hashSet1.Add(4);
hashSet1.Add(5);
var hashSet2 = new HashSet<int>();
hashSet2.Add(6);
hashSet2.Add(7);
hashSet2.Add(8);
hashSet2.Add(9);
hashSet2.Add(0);
SwapHashSets(hashSet1, hashSet2, 3);
}
private List<int> GetXValuesFromHashSet(HashSet<int> hashSet, int count)
{
var list = new List<int>();
for (var i = 0; i < count; i++)
{
list.Add(hashSet.ElementAt(i));
}
return list;
}
private void SwapHashSets(HashSet<int> hashSet1, HashSet<int> hashSet2, int count )
{
var list1 = GetXValuesFromHashSet(hashSet1, count);
var list2 = GetXValuesFromHashSet(hashSet2, count);
foreach (var value in list1)
{
hashSet1.Remove(value);
}
foreach (var value in list2)
{
hashSet2.Remove(value);
}
foreach (var value in list1)
{
hashSet2.Add(value);
}
foreach (var value in list2)
{
hashSet1.Add(value);
}
}

Related

Take next item from list while iterating with another list

I have two list
List<string> listA;
List<string> listB;
How to get next item of listA when im iterating with listB? Pseudocode:
List<dynamic> listC = new List<dynamic>();
foreach (var elementA in listA)
{
listC.Add(new
{
a: elementA,
b: listB.TakeNextItem() // how to achive this?
});
}
You could use Enumerable.Zip:
var listC = listA
.Zip(listB, (a, b) => new { a, b })
.ToList();
This iterates over both lists and projects items into a new list.
It also statically types your listC variable, rather than using dynamic.
You could use a for loop instead of a foreach loop and then use the current index to access both listA and listB at the same time.
List<dynamic> listC = new List<dynamic>();
// When listA Count is as bigger as listB don't execute for-loop
if (listA.Count > listB.Count) {
return;
}
for (int i = 0; i < listA.Count; i++) {
listC.Add(new {
a = listA[i],
b = listB[i]
});
}

What is the correct way to sort List <string> using C#?

I have LIST1 <> and LIST2 <> and like to compare these two lists. Followings are my conditions..
1-If LIST1 and LIST2 have the same items than add same items to LIST3
2-If LIST1 doesnt contain LIST2 items than add different items to LIST4
3-if LIST2 doesnt contain LIST1 items than add different items to LIST5
lets say my result is like below depends on the conditions;
LIST1<string> = A,B,C,D
LIST2<string> = A,K,F,C
LIST3<string> = A,C
LIST4<string> = B,D
LIST5<string> = K,F
here is my code;
foreach (string src in LIST1)
{
foreach (string trg in LIST2)
{
if (LIST1.ToString() == LIST2.ToString())
{
LIST3.Add(LIST1.ToString());
}
else
{
LIST4.Clear();
foreach (string l3 in LIST1)
{
if (!LIST2.Contains(l3))
LIST4.Add(l3);
}
LIST5.Clear();
foreach (string l4 in LIST2)
{
if (!LIST1.Contains(l4))
{
LIST5.Add(l4);
}
}
}
}
}
A quick way to do this would be:
var list3 = list1.Intersect(list2).ToList();
var list4 = list1.Except(list2).ToList();
var list5 = list2.Except(list1).ToList();
Update: If you have to do with larger lists (and/or have to write this in multiple places), you can write an extension method like below:
public static Tuple<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> Diff<T>(
this IEnumerable<T> first, IEnumerable<T> second)
{
var intersection = new List<T>();
var onlyInFirst = new HashSet<T>();
var onlyInSecond = new HashSet<T>(second);
foreach (var item in first)
{
if (onlyInSecond.Remove(item)) intersection.Add(item);
else onlyInFirst.Add(item);
}
return Tuple.Create<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>>
(intersection, onlyInFirst, onlyInSecond);
}
This method returns a tuple of three IEnumerable<T>s representing the set of intersection, set of items only in the first collection, and set of items only in the second collection; respectively.
Usage:
var list1 = new[] { "A", "B", "C", "D" };
var list2 = new[] { "A", "K", "F", "C" };
var diff = list1.Diff(list2);
// diff.Item1 = A,C (intersection)
// diff.Item2 = B,D (only in first)
// diff.Item3 = K,F (only in second)
Not sure what this has to do with sorting, but here's Linq statements for each condition:
List3 = List1.Intersect(List2).ToList();
List4 = List1.Where(l1 => !List2.Any(l2 => l2 == l1)).ToList();
List5 = List2.Where(l2 => !List1.Any(l1 => l2 == l1)).ToList();
as pointed out in comments Except will work too:
List4 = List1.Except(List2).ToList();
List5 = List2.Except(List1).ToList();

How to combine multiple lists with custom sequence

Not sure if there's a algorithm to describe this problem but are there any elegant methods to combine the list in a custom sequence. For example:
List<string> list1 = new List<string>();
List<string> list2 = new List<string>();
List<string> list3 = new List<string>();
list1.Add("a");
list1.Add("b");
list1.Add("c");
list2.Add("d");
list2.Add("e");
list2.Add("f");
list3.Add("g");
list3.Add("h");
list3.Add("i");
List<string> combined = new List<string>();
I would like the contents of combined to contain a sequence as follows:
a //First record in list1
d //First record in list2
g //First record in list3
b //Second record in list1
e //Second record in list2
h //Second record in list3
c //Third record in list1
f //Third record in list2
i //Third record in list3
The number of records in each list may not be equal.
EDIT
When the number of records in each list may not be equal i mean:
List<string> list1 = new List<string>();
List<string> list2 = new List<string>();
List<string> list3 = new List<string>();
list1.Add("a");
list2.Add("b");
list2.Add("c");
list3.Add("d");
list3.Add("e");
list3.Add("f");
List<string> combined = new List<string>();
Expected results:
a //First record in list1
b //First record in list2
d //First record in list3
c //Second record in list2
e //Second record in list3
f //Third record in list3
Not sure if there's a name. Merging? Splicing? But the code is easy.
var lists = new [] { list1, list2, list3 };
var combined = new List<string>(lists.Sum(l => l.Count));
for (var i = 0; i < lists.Max(l => l.Count); i++)
{
foreach (var list in lists)
{
if (i < list.Count)
combined.Add (list[i])
}
}
int MaxCount = List1.Count; //Or whatever the highest list count.
for (int i = 0; i < MaxCount; i++)
{
if( list1.Count > i)
combined.Add(list1[i]);
if( list2.Count > i)
combined.Add(list2[i]);
if( list3.Count > i)
combined.Add(list3[i]);
}
Unfortunately there are no built in function for it.
You should loop each list and add it to a new custom list.
Example:
List<string> result = new List<string>();
int listLength = Math.Max(list1.Count, Math.Max(list2.Count, list3.Count)); // get the largest list length
for(int index = 0; index < listLength; i++){
if(list1.Count > index) result.Add(list1[index);
if(list2.Count > index) result.Add(list3[index);
if(list3.Count > index) result.Add(list3[index);
}
You could use this generic Merge<T> function:
public static IEnumerable<T> Merge<T>(params List<T>[] lists)
{
var max = lists.Max(list => list.Count());
for (int i = 0; i < max; i++)
{
foreach (var list in lists)
{
if (i < list.Count)
{
yield return list[i];
}
}
}
}
Example usage:
var merged = Merge(list1, list2, list3).ToList();
A quick LINQPad example:
void Main()
{
List<string> list1 = new List<string>();
List<string> list2 = new List<string>();
List<string> list3 = new List<string>();
list1.Add("a");
list1.Add("b");
list1.Add("c");
list2.Add("d");
list2.Add("e");
list2.Add("f");
list3.Add("g");
list3.Add("h");
list3.Add("i");
Merge(new[] { list1, list2, list3}, (c1, c2) => c1 + c2).SelectMany(s => s).Dump();
}
IEnumerable<T> Merge<T>(IEnumerable<IEnumerable<T>> sources, Func<T, T, T> combine)
{
return sources.Aggregate((s1, s2) => s1.Zip(s2, combine));
}
The result is an IEnumerable<char> but this is simple enough to convert into a List<string> if necessary.
You can either use Concate() or AddRange, see this thread for more info about the difference between these methods

find common items across multiple lists in C#

I have two generic list :
List<string> TestList1 = new List<string>();
List<string> TestList2 = new List<string>();
TestList1.Add("1");
TestList1.Add("2");
TestList1.Add("3");
TestList2.Add("3");
TestList2.Add("4");
TestList2.Add("5");
What is the fastest way to find common items across these lists?
Assuming you use a version of .Net that has LINQ, you can use the Intersect extension method:
var CommonList = TestList1.Intersect(TestList2)
If you have lists of objects and want to get the common objects for some property then use;
var commons = TestList1.Select(s1 => s1.SomeProperty).ToList().Intersect(TestList2.Select(s2 => s2.SomeProperty).ToList()).ToList();
Note: SomeProperty refers to some criteria you want to implement.
Assuming you have LINQ available. I don't know if it's the fastest, but a clean way would be something like:
var distinctStrings = TestList1.Union(TestList2).Distinct();
var distinctStrings = TestList1.Union(TestList2);
Update: well never mind my answer, I've just learnt about Intersect as well!
According to an update in the comments, Unions apply a distinct, which makes sense now that I think about it.
You can do this by counting occurrences of all items in all lists - those items whose occurrence count is equal to the number of lists, are common to all lists:
static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
{
Dictionary<T, int> map = new Dictionary<T, int>();
int listCount = 0; // number of lists
foreach (IEnumerable<T> list in lists)
{
listCount++;
foreach (T item in list)
{
// Item encountered, increment count
int currCount;
if (!map.TryGetValue(item, out currCount))
currCount = 0;
currCount++;
map[item] = currCount;
}
}
List<T> result= new List<T>();
foreach (KeyValuePair<T,int> kvp in map)
{
// Items whose occurrence count is equal to the number of lists are common to all the lists
if (kvp.Value == listCount)
result.Add(kvp.Key);
}
return result;
}
Sort both arrays and start from the top of both and compare if they are equal.
Using a hash is even faster: Put the first array in a hash, then compare every item of the second array if it is already in the hash.
I don't know those Intersect and Union are implemented. Try to find out their running time if you care about the performance. Of course they are better suited if you need clean code.
Use the Intersect method:
IEnumerable<string> result = TestList1.Intersect(TestList2);
Using HashSet for fast lookup. Here is the solution:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<int> list1 = new List<int> {1, 2, 3, 4, 5, 6 };
List<int> list2 = new List<int> {1, 2, 3 };
List<int> list3 = new List<int> {1, 2 };
var lists = new IEnumerable<int>[] {list1, list2, list3 };
var commons = GetCommonItems(lists);
Console.WriteLine("Common integers:");
foreach (var c in commons)
Console.WriteLine(c);
}
static IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
{
HashSet<T> hs = new HashSet<T>(lists.First());
for (int i = 1; i < lists.Length; i++)
hs.IntersectWith(lists[i]);
return hs;
}
}
Following the lead of #logicnp on counting the number of lists containing each member, once you have your list of lists, it's pretty much one line of code:
List<int> l1, l2, l3, cmn;
List<List<int>> all;
l1 = new List<int>() { 1, 2, 3, 4, 5 };
l2 = new List<int>() { 1, 2, 3, 4 };
l3 = new List<int>() { 1, 2, 3 };
all = new List<List<int>>() { l1, l2, l3 };
cmn = all.SelectMany(x => x).Distinct()
.Where(x => all .Select(y => (y.Contains(x) ? 1 : 0))
.Sum() == all.Count).ToList();
Or, if you prefer:
public static List<T> FindCommon<T>(IEnumerable<List<T>> Lists)
{
return Lists.SelectMany(x => x).Distinct()
.Where(x => Lists.Select(y => (y.Contains(x) ? 1 : 0))
.Sum() == Lists.Count()).ToList();
}

A method to count occurrences in a list

Is there a simple way to count the number of occurrences of all elements of a list into that same list in C#?
Something like this:
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
string Occur;
List<string> Words = new List<string>();
List<string> Occurrences = new List<string>();
// ~170 elements added. . .
for (int i = 0;i<Words.Count;i++){
Words = Words.Distinct().ToList();
for (int ii = 0;ii<Words.Count;ii++){Occur = new Regex(Words[ii]).Matches(Words[]).Count;}
Occurrences.Add (Occur);
Console.Write("{0} ({1}), ", Words[i], Occurrences[i]);
}
}
How about something like this ...
var l1 = new List<int>() { 1,2,3,4,5,2,2,2,4,4,4,1 };
var g = l1.GroupBy( i => i );
foreach( var grp in g )
{
Console.WriteLine( "{0} {1}", grp.Key, grp.Count() );
}
Edit per comment: I will try and do this justice. :)
In my example, it's a Func<int, TKey> because my list is ints. So, I'm telling GroupBy how to group my items. The Func takes a int and returns the the key for my grouping. In this case, I will get an IGrouping<int,int> (a grouping of ints keyed by an int). If I changed it to (i => i.ToString() ) for example, I would be keying my grouping by a string. You can imagine a less trivial example than keying by "1", "2", "3" ... maybe I make a function that returns "one", "two", "three" to be my keys ...
private string SampleMethod( int i )
{
// magically return "One" if i == 1, "Two" if i == 2, etc.
}
So, that's a Func that would take an int and return a string, just like ...
i => // magically return "One" if i == 1, "Two" if i == 2, etc.
But, since the original question called for knowing the original list value and it's count, I just used an integer to key my integer grouping to make my example simpler.
You can do something like this to count from a list of things.
IList<String> names = new List<string>() { "ToString", "Format" };
IEnumerable<String> methodNames = typeof(String).GetMethods().Select(x => x.Name);
int count = methodNames.Where(x => names.Contains(x)).Count();
To count a single element
string occur = "Test1";
IList<String> words = new List<string>() {"Test1","Test2","Test3","Test1"};
int count = words.Where(x => x.Equals(occur)).Count();
var wordCount =
from word in words
group word by word into g
select new { g.Key, Count = g.Count() };
This is taken from one of the examples in the linqpad
public void printsOccurences(List<String> words)
{
var selectQuery =
from word in words
group word by word into g
select new {Word = g.Key, Count = g.Count()};
foreach(var word in selectQuery)
Console.WriteLine($"{word.Word}: {word.Count}");*emphasized text*
}
This is a version which avoids Linq but uses only slightly more code.
// using System.Collections.Generic;
Dictionary<int, int> oGroups = new Dictionary<int, int>();
List<int> oList = new List<int>() { 1, 2, 3, 4, 5, 2, 2, 2, 4, 4, 4, 1 };
foreach (int iCurrentValue in oList)
{
if (oGroups.ContainsKey(iCurrentValue))
oGroups[iCurrentValue]++;
else
oGroups.Add(iCurrentValue, 1);
}
foreach (KeyValuePair<int, int> oGroup in oGroups)
{
Console.WriteLine($"Value {oGroup.Key} appears {oGroup.Value} times.");
}
this code returns a dictionary that contain the world and the occurrence:
var wordsDic = Words
.GroupBy(p => p)
.ToDictionary(p => p.Key, q => q.Count());
Your outer loop is looping over all the words in the list. It's unnecessary and will cause you problems. Remove it and it should work properly.

Categories