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
Related
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]
});
}
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);
}
}
This is remarkably similar to another question I asked previously. I have no idea how to do things in Linq so I need some help with this one. I want to find the Modal value of a List> for each inner value.
I have the following list:
List<List<double>> myFullList = new List<List<double>>();
for(int i = 1; i <= numberOfLoops; i++)
{
List<double> myInnerList = new List<double>();
for(int i = 1; i <= 10; i++)
{
// Populate inner list with random numbers
myInnerList.Add(double myRandomNumber);
}
// Add the inner list to the full list
myFullList.Add(myInnerList);
}
The list should look something like this:
myFullList[0] = {rand#1,rand#2,rand#3,...,rand#10}
myFulllist[1] = {rand#1,rand#2,rand#3,...,rand#10}
.
.
.
.
myFulllist[1] = {rand#1,rand#2,rand#3,...,rand#10}
I need to find the MODAL VALUE for that data to form ONE single list that looks something like this:
List<double> mode= new List<double>();
mode= {mode#1, mode#2........mode#10}
This output variable will find the mode of the data for the same "row" of data in the inner list.
Simple example:
innerList[0] = {1.00,2.00,3.00};
innerList[1] = {3.00,2.00,8.00};
innerList[2] = {3.00,9.00,1.00};
innerList[3] = {3.00,1.00,1};
fullList = {innerList[0], innerList[1], innerList[2], innerList[3]};
modeList = {3,2,1};
Not the most elegant way, but probably easier to Understand. It has been succesfully tested :)
class Program
{
static void Main(string[] args)
{
Random rnd = new Random();
int numberOfLoops = 10;
List<List<int>> myFullList = new List<List<int>>();
for (int i = 0; i < numberOfLoops; i++)
{
List<int> myInnerList = new List<int>();
for (int j = 0; j < 10; j++)
{
// Populate inner list with random numbers
myInnerList.Add(rnd.Next(0, 10));
}
// Add the inner list to the full list
myFullList.Add(myInnerList);
}
myFullList = Transpose<int>(myFullList);
List<int> result = new List<int>();
foreach (List<int> subList in myFullList)
result.Add(Mode(subList));
//TO-DO: linq version!
//List<int> result = myFullList.ForEach(num => Mode(num));
}
public static int Mode(List<int> x)
{
int mode = x.GroupBy(v => v)
.OrderByDescending(g => g.Count())
.First()
.Key;
return mode;
}
public static List<List<T>> Transpose<T>(List<List<T>> lists)
{
var longest = lists.Any() ? lists.Max(l => l.Count) : 0;
List<List<T>> outer = new List<List<T>>(longest);
for (int i = 0; i < longest; i++)
outer.Add(new List<T>(lists.Count));
for (int j = 0; j < lists.Count; j++)
for (int i = 0; i < longest; i++)
outer[i].Add(lists[j].Count > i ? lists[j][i] : default(T));
return outer;
}
}
That's quiet simple, here is code (sorry, haven't fully tested it, but it's good to start with):
public static class ModalHelper
{
public static List<double> GetModals(List<List<double>> source)
{
return source.Select(list => list.Sum()/list.Count).ToList();
}
}
This linq query should do the trick
var result = list.Select<List<double>, List<KeyValuePair<int, double>>>(sub =>
{
List<KeyValuePair<int, double>> elems = new List<KeyValuePair<int, double>>(sub.Count);
for (int i = 0; i < sub.Count; ++i)
elems.Add(new KeyValuePair<int, double>(i, sub[i]));
return elems;
}).SelectMany((x) => x).GroupBy((x) => x.Key).Select<IGrouping<int, KeyValuePair<int, double>>, double>(x =>
{
var y = x.GroupBy(g => g.Value).OrderByDescending(g => g.Count());
return y.First().First().Value;
});
Here is an example:
static void Main(string[] args)
{
List<List<double>> list = new List<List<double>>();
list.Add(new List<double> { 1.00, 2.00, 3.00 });
list.Add(new List<double> { 3.00, 2.00, 8.00 });
list.Add(new List<double> { 3.00, 9.00, 1.00 });
list.Add(new List<double> { 3.00, 1.00, 1 });
var result = list.Select<List<double>, List<KeyValuePair<int, double>>>(sub =>
{
List<KeyValuePair<int, double>> elems = new List<KeyValuePair<int, double>>(sub.Count);
for (int i = 0; i < sub.Count; ++i)
elems.Add(new KeyValuePair<int, double>(i, sub[i]));
return elems;
}).SelectMany((x) => x).GroupBy((x) => x.Key).Select<IGrouping<int, KeyValuePair<int, double>>, double>(x =>
{
var y = x.GroupBy(g => g.Value).OrderByDescending(g => g.Count());
return y.First().First().Value;
});
foreach (double val in result)
Console.Write(val + " ");
Console.WriteLine();
}
Here a live version at ideone: http://ideone.com/ye2EhG
First the lists are transformed to lists of key-value-pairs which add the information of the index inside each list. Then these lists are flattened to one single list and then this new list is grouped by the index. The groups are ordered by the count of values and the most-frequent element is returned for each group.
Something like this should give the mode:
var temp = myFullList.SelectMany(l => l).GroupBy(all => all).Select(result => new
{
Value = result.Key,
Count = result.Count()
}).OrderByDescending(t => t.Count);
Explanation:
From MSDN - The SelectMany
Projects each element of a sequence to an IEnumerable and flattens
the resulting sequences into one sequence.
So it gives us each decimal from the sub lists. We then group that by the decimals themselves and select the count for each along with their value. Finally we order by the count to give the most frequently occurring decimals first.
Edit based on the comment from Robert S
It seems the above code isn't what was required. As Robert S points out that code gives the mode of ALL numbers in the List<List<double>> but the question is how to get the mode from each column.
The following code should give the mode per column. Note that this code ignores duplicates; if more than one number appears the same amount of times the first number will be given:
var result1 = myFullList[0].Select((l, i) => new
{
Column = i,
Mode = myFullList.GroupBy(fl => fl[i]).OrderByDescending(t => t.Count()).Select(t => t.Key).FirstOrDefault()
});
foreach (var item in result1)
{
Console.WriteLine(string.Format("{0} {1}", item.Column, item.Mode));
}
The code is using the overload of Select to take the index of the element (the column in the OP's definition). It then groups each item at that index. Note there are no bounds checks on myFullList but in production code there should be.
If duplicates are an issue we need two steps:
var temp2 = myFullList[0].Select((l, i) => new
{
Column = i,
Mode = myFullList.GroupBy(fl => fl[i]).Select(t => new { Number = t.Key, Count = t.Count() }).OrderByDescending(a => a.Count)
});
var result2 = temp2.Select(t => new
{
Column = t.Column,
Mode = t.Mode.Where(m => m.Count == t.Mode.Max(tm => tm.Count))
});
foreach (var item in result2)
{
for (int i = 0; i < item.Mode.Count(); i++)
{
Console.WriteLine(string.Format("{0} {1}", item.Column, item.Mode.ElementAt(i)));
}
}
In the above code temp2.Mode will contain an IEnumerable of an anonymous object containing the number and how many times that number has appeared. result2 is then populated by grabbing each of those items where the count matches the max of the count.
Given the input:
myFullList.Add(new List<double> { 1.00, 2.00, 3.00 });
myFullList.Add(new List<double> { 3.00, 2.00, 3.00 });
myFullList.Add(new List<double> { 3.00, 9.00, 1.00 });
myFullList.Add(new List<double> { 3.00, 1.00, 1 });
The first code outputs
0 3
1 2
2 3
and the second outputs
0 3
1 2
2 3
2 1
Note we have two outputs for column 2 as both 3 and 1 are equally popular.
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();
This question already has answers here:
C# LINQ find duplicates in List
(13 answers)
Closed 3 years ago.
I have a List<string> which has some words duplicated. I need to find all words which are duplicates.
Any trick to get them all?
In .NET framework 3.5 and above you can use Enumerable.GroupBy which returns an enumerable of enumerables of duplicate keys, and then filter out any of the enumerables that have a Count of <=1, then select their keys to get back down to a single enumerable:
var duplicateKeys = list.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key);
If you are using LINQ, you can use the following query:
var duplicateItems = from x in list
group x by x into grouped
where grouped.Count() > 1
select grouped.Key;
or, if you prefer it without the syntactic sugar:
var duplicateItems = list.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key);
This groups all elements that are the same, and then filters to only those groups with more than one element. Finally it selects just the key from those groups as you don't need the count.
If you're prefer not to use LINQ, you can use this extension method:
public void SomeMethod {
var duplicateItems = list.GetDuplicates();
…
}
public static IEnumerable<T> GetDuplicates<T>(this IEnumerable<T> source) {
HashSet<T> itemsSeen = new HashSet<T>();
HashSet<T> itemsYielded = new HashSet<T>();
foreach (T item in source) {
if (!itemsSeen.Add(item)) {
if (itemsYielded.Add(item)) {
yield return item;
}
}
}
}
This keeps track of items it has seen and yielded. If it hasn't seen an item before, it adds it to the list of seen items, otherwise it ignores it. If it hasn't yielded an item before, it yields it, otherwise it ignores it.
and without the LINQ:
string[] ss = {"1","1","1"};
var myList = new List<string>();
var duplicates = new List<string>();
foreach (var s in ss)
{
if (!myList.Contains(s))
myList.Add(s);
else
duplicates.Add(s);
}
// show list without duplicates
foreach (var s in myList)
Console.WriteLine(s);
// show duplicates list
foreach (var s in duplicates)
Console.WriteLine(s);
If you're looking for a more generic method:
public static List<U> FindDuplicates<T, U>(this List<T> list, Func<T, U> keySelector)
{
return list.GroupBy(keySelector)
.Where(group => group.Count() > 1)
.Select(group => group.Key).ToList();
}
EDIT: Here's an example:
public class Person {
public string Name {get;set;}
public int Age {get;set;}
}
List<Person> list = new List<Person>() { new Person() { Name = "John", Age = 22 }, new Person() { Name = "John", Age = 30 }, new Person() { Name = "Jack", Age = 30 } };
var duplicateNames = list.FindDuplicates(p => p.Name);
var duplicateAges = list.FindDuplicates(p => p.Age);
foreach(var dupName in duplicateNames) {
Console.WriteLine(dupName); // Will print out John
}
foreach(var dupAge in duplicateAges) {
Console.WriteLine(dupAge); // Will print out 30
}
Using LINQ, ofcourse.
The below code would give you dictionary of item as string, and the count of each item in your sourc list.
var item2ItemCount = list.GroupBy(item => item).ToDictionary(x=>x.Key,x=>x.Count());
For what it's worth, here is my way:
List<string> list = new List<string>(new string[] { "cat", "Dog", "parrot", "dog", "parrot", "goat", "parrot", "horse", "goat" });
Dictionary<string, int> wordCount = new Dictionary<string, int>();
//count them all:
list.ForEach(word =>
{
string key = word.ToLower();
if (!wordCount.ContainsKey(key))
wordCount.Add(key, 0);
wordCount[key]++;
});
//remove words appearing only once:
wordCount.Keys.ToList().FindAll(word => wordCount[word] == 1).ForEach(key => wordCount.Remove(key));
Console.WriteLine(string.Format("Found {0} duplicates in the list:", wordCount.Count));
wordCount.Keys.ToList().ForEach(key => Console.WriteLine(string.Format("{0} appears {1} times", key, wordCount[key])));
I'm assuming each string in your list contains several words, let me know if that's incorrect.
List<string> list = File.RealAllLines("foobar.txt").ToList();
var words = from line in list
from word in line.Split(new[] { ' ', ';', ',', '.', ':', '(', ')' }, StringSplitOptions.RemoveEmptyEntries)
select word;
var duplicateWords = from w in words
group w by w.ToLower() into g
where g.Count() > 1
select new
{
Word = g.Key,
Count = g.Count()
}
I use a method like that to check duplicated entrys in a string:
public static IEnumerable<string> CheckForDuplicated(IEnumerable<string> listString)
{
List<string> duplicateKeys = new List<string>();
List<string> notDuplicateKeys = new List<string>();
foreach (var text in listString)
{
if (notDuplicateKeys.Contains(text))
{
duplicateKeys.Add(text);
}
else
{
notDuplicateKeys.Add(text);
}
}
return duplicateKeys;
}
Maybe it's not the most shorted or elegant way, but I think that is very readable.
lblrepeated.Text = "";
string value = txtInput.Text;
char[] arr = value.ToCharArray();
char[] crr=new char[1];
int count1 = 0;
for (int i = 0; i < arr.Length; i++)
{
int count = 0;
char letter=arr[i];
for (int j = 0; j < arr.Length; j++)
{
char letter3 = arr[j];
if (letter == letter3)
{
count++;
}
}
if (count1 < count)
{
Array.Resize<char>(ref crr,0);
int count2 = 0;
for(int l = 0;l < crr.Length;l++)
{
if (crr[l] == letter)
count2++;
}
if (count2 == 0)
{
Array.Resize<char>(ref crr, crr.Length + 1);
crr[crr.Length-1] = letter;
}
count1 = count;
}
else if (count1 == count)
{
int count2 = 0;
for (int l = 0; l < crr.Length; l++)
{
if (crr[l] == letter)
count2++;
}
if (count2 == 0)
{
Array.Resize<char>(ref crr, crr.Length + 1);
crr[crr.Length - 1] = letter;
}
count1 = count;
}
}
for (int k = 0; k < crr.Length; k++)
lblrepeated.Text = lblrepeated.Text + crr[k] + count1.ToString();