remove distinct items - c#

how to remove distinct items in list from another list in c# ?

You could use Except like so:
var result = list2.Except(list1).ToList();
So an example would be:
List<int> a = new List<int>() { 1, 2, 3, 4, 5 };
List<int> b = new List<int>() { 1, 2, 3, 4 };
List<int> c = a.Except(b).ToList();
Where List C would only have the value 5 in it.

Not as elegant as using Except (which I never knew existed)... but this works:
List<string> listA = new List<string>();
List<string> listB = new List<string>();
listA.Add("A");
listA.Add("B");
listA.Add("C");
listA.Add("D");
listB.Add("B");
listB.Add("D");
for (int i = listA.Count - 1; i >= 0; --i)
{
int matchingIndex = listB.LastIndexOf(listA[i]);
if (matchingIndex != -1)
listB.RemoveAt(matchingIndex);
}

var distinctItems = items.Distinct();
Not quite what you asked for, but it's a lot easier to make a new list by copying items that you want to keep, than trying to edit the original list.
If you want to control what constitutes "equality" for your list items, then call the overload that accepts an instance of IEqualityComparer<T>.
See MSDN.

Related

Creating new list from two lists that contains only distinct items?

I have two lists contains some values.I want to create new list and add all the values from two lists that all are distinct.
I am new to LINQ and not experienced in it yet. so i done that in this way but this is not what i wanted.
The new list only contains that distinct values if the value is present in both list then that value will be only one time in the new list.
I acheieved it by using Distinct Extension method but that is not what i wanted...i want that new list only contain the Distinct values.
Code:
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
List<string> firstList_names = new List<string>()
{
"Rehan",
"Hamza",
"Adil",
"Arif",
"Hamid",
"Hadeed"
};
List<string> secondList_names = new List<string>()
{
"Mahboob",
"Zeeshan",
"Rizwan",
"Hamid",
"Rehan",
"Hamza"
};
List<string> result = new List<string>();
foreach (var nameOfFirstList in firstList_names)
result.Add(nameOfFirstList);
foreach (var namesOfSecondList in secondList_names)
result.Add(namesOfSecondList);
Console.Write(result.Distinct().Count());
}
}
}
Follow this example (taken from https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.union?view=netframework-4.7.2)
int[] ints1 = { 5, 3, 9, 7, 5, 9, 3, 7 };
int[] ints2 = { 8, 3, 6, 4, 4, 9, 1, 0 };
IEnumerable<int> union = ints1.Union(ints2);
foreach (int num in union)
{
Console.Write("{0} ", num);
}
/*
This code produces the following output:
5 3 9 7 8 6 4 1 0
*/
In order to get only the items common to both lists, you can use the System.Linq extension method Enumerable.Intersect, which returns the intersection of two lists:
var intersection = firstList_names.Intersect(secondList_names);
There is some confusion on your question, however. If you want all the items from both lists (without any duplicates), then you can use the Union extension method:
var union = firstList_names.Union(secondList_names);
If you want to do these the "old fashioned" way (without the extension methods), you could do something like the samples below.
For Intersection:
var intersection = new List<string>();
foreach(var item in firstList_names)
{
if (secondList_names.Contains(item) && !intersection.Contains(item))
{
intersection.Add(item);
}
}
For Union:
// Start with a list of the distinct firstList_names
var union = new List<string>();
foreach(var item in firstList_names)
{
if (!union.Contains(item))
{
union.Add(item);
}
}
// Add any items from the secondList_names that don't exist
foreach (var item in secondList_names)
{
if (!union.Contains(item))
{
union.Add(item);
}
}

want to remove "0" at index 0 in a list but not 10,20,30..... C#

my code
...
List<int> a = new List<int>();
List<int> b = new List<int>();
...
some boxes for input
...
var match = a.Intersect(b);
string output = string.Join(",", match);
foreach (var x in output)
{
label.Text += Regex.Replace(Convert.ToString(x), "[0]", "");
}
but this deletes all 0's
if I enter 0, 10, 100
I only wanna delete the first 0 (index0 as List is sorted) but I can't make an expression saying that it should remove index0 all the time, because i depends on, if the users enter a 0... so it should look for a 0 at first index and (only) if its there... remove it.
I can't wrap my head around this.
Thanks in advance
Don't know why you need regex and the loop over the chars. Here are 3 ways of doing so:
List<int> a = new List<int> { 0, 10, 20, 5 };
List<int> b = new List<int> { 0, 10, 20 };
//Option 1 - .Remove() on one of the lists
a.Remove(0);
var matches = a.Intersect(b);
//Option 2 - .Remove() on intersection
var matches = a.Intersect(b).ToList();
matches.Remove(0);
//Option 3 - .Where()
var matches = a.Where(item => item != 0)
.Intersect(b);
var text = string.Join(",", matches); //For both ways: 10,20

c# Dictionary with HashSet<int> as value get intersection of all

I have a Dictionary with HashSet as Value. I have an int[] with the keys for which I want to get the Count of common values in the HashSet's.
Here is a piece of code that works in a very inefficient way as it requires to create a HashSet and modify it in memory before the final Count.
Dictionary<int, HashSet<int>> d = new Dictionary<int, HashSet<int>>();
HashSet<int> s1 = new HashSet<int>() { 3, 4, 5, 6, 7, 8, 9 };
HashSet<int> s2 = new HashSet<int>() { 1, 2, 3, 4, 5, 8 };
HashSet<int> s3 = new HashSet<int>() { 1, 3, 5, 10, 15, 20 };
HashSet<int> s4 = new HashSet<int>() { 1, 20 };
d.Add(10, s1);
d.Add(15, s2);
d.Add(20, s3);
d.Add(25, s4);
// List of keys from which I need the intersection of the HashSet's
int[] l = new int[3] { 10, 15, 20 };
// Get an IEnumerator with the HashSet from the values of the selected Dictionary entries (10,15,20 selects s1, s2 and s3)
var hashlist = d.Where(x => l.Contains(x.Key));
// Create a new HashSet to contain the intersection of all the HashSet's
HashSet<int> first = new HashSet<int>(hashlist.First().Value);
foreach (var hash in hashlist.Skip(1))
first.IntersectWith(hash.Value);
// Show the number of common int's
Console.WriteLine("Common elements: {0}", first.Count);
What I am looking for is an efficient way (LinQ perhaps?) to count the common elements without having to create a new HashSet as I am running a similar code hundreds of millions of times.
It is also important to note that I create a new HashSet to get the intersections as I do not want to modify the original HashSet's.
Best regargs,
Jorge
What I am looking for is an efficient way (LinQ perhaps?) to count the common elements
If you really wish maximum performance, forget about LINQ, here is an old school way with all possible optimizations (that I can think of) applied:
// Collect the non empty matching sets, keeping the set with the min Count at position 0
var sets = new HashSet<int>[l.Length];
int setCount = 0;
foreach (var key in l)
{
HashSet<int> set;
if (!d.TryGetValue(key, out set) || set.Count == 0) continue;
if (setCount == 0 || sets[0].Count <= set.Count)
sets[setCount++] = set;
else
{
sets[setCount++] = sets[0];
sets[0] = set;
}
}
int commonCount = 0;
if (setCount > 0)
{
if (setCount == 1)
commonCount = sets[0].Count;
else
{
foreach (var item in sets[0])
{
bool isCommon = true;
for (int i = 1; i < setCount; i++)
if (!sets[i].Contains(item)) { isCommon = false; break; }
if (isCommon) commonCount++;
}
}
}
Console.WriteLine("Common elements: {0}", commonCount);
Hope the code is self explanatory.
This can definitely be improved:
var hashlist = d.Where(x => l.Contains(x.Key));
By rewriting it as:
var hashlist = l.Select(x => d[x]);
This will take advantage of the Dictionary's internal HashSet to efficiently get the value at the specific key rather than repeatedly iterating over the int[].
Your next big problem is that Linq is lazy, so by calling Fist() and Skip(1) separately, you're actually requiring multiple enumerations over the collection using the previously mentioned Where(…) filter.
To avoid multiple enumerations, you could rewrite this:
HashSet<int> first = new HashSet<int>(hashlist.First().Value);
foreach (var hash in hashlist.Skip(1))
first.IntersectWith(hash.Value);
As:
var intersection = hashlist.Aggregate(
(HashSet<int>)null,
(h, j) =>
{
if (h == null)
h = new HashSet<int>(j);
else
h.IntersectWith(j);
return h;
});
But depending on your precise use case it may just be faster (and easier to understand) to simply materialize the result into a List first, then use a simple for loop:
var hashlist = l.Select(x => d[x]).ToList();
HashSet<int> first = hashlist[0];
for (var i = 0; i < hashlist.Count; i++)
first.IntersectWith(hashlist[i]);
Here's a quick benchmark with these various options (your results may vary):
Original 2.285680 (ms)
SelectHashList 1.912829
Aggregate 1.815872
ToListForLoop 1.608565
OrderEnumerator 1.975067 // Scott Chamberlain's answer
EnumeratorOnly 1.732784 // Scott Chamberlain's answer without the call to OrderBy()
AggIntersect 2.046930 // P. Kouvarakis's answer (with compiler error fixed)
JustCount 1.260448 // Ivan Stoev's updated answer
There are a few tricks you could do that could potentially buy you a lot of speed up. The biggest one I see is start with the smallest set first, then work your way up to larger ones, this gives the initial set the smallest possible amount of stuff to intersect with, giving faster lookups.
Also, if you manually build your ienumerable instead of using a foreach you don't need to enumerate the list twice (EDIT: also use the trick p.s.w.g mentioned, select against the dictionary instead of using a .Contains().
Important Note: this method will only give you benefits if you are combining a large number of HashSets with a wide range of item counts. The overhead of calling OrderBy will be significant and in a small dataset like you have in your example and it is unlikely you will see any benefit.
Dictionary<int, HashSet<int>> d = new Dictionary<int, HashSet<int>>();
HashSet<int> s1 = new HashSet<int>() { 3, 4, 5, 6, 7, 8, 9 };
HashSet<int> s2 = new HashSet<int>() { 1, 2, 3, 4, 5, 8 };
HashSet<int> s3 = new HashSet<int>() { 1, 3, 5, 10, 15, 20 };
HashSet<int> s4 = new HashSet<int>() { 1, 20 };
d.Add(10, s1);
d.Add(15, s2);
d.Add(20, s3);
d.Add(25, s4);
// List of keys from which I need the intersection of the HashSet's
int[] l = new int[3] { 10, 15, 20 };
HashSet<int> combined;
//Sort in increasing order by count
//Also used the trick from p.s.w.g's answer to get a better select.
IEnumerable<HashSet<int>> sortedList = l.Select(x => d[x]).OrderBy(x => x.Count);
using (var enumerator = sortedList.GetEnumerator())
{
if (enumerator.MoveNext())
{
combined = new HashSet<int>(enumerator.Current);
}
else
{
combined = new HashSet<int>();
}
while (enumerator.MoveNext())
{
combined.IntersectWith(enumerator.Current);
}
}
// Show the number of common int's
Console.WriteLine("Common elements: {0}", combined.Count);
`IntersectWith()' is probably as efficient as you can get.
Using LINQ you could make code cleaner (?):
var result = l.Aggregate(null, (acc, key) => acc == null? d[key] : acc.Intersect(d[key]));

How to count how many times exist each number from int[] inside IEnumerable<int>?

I have array of ints(Call him A) and IEnumarable(Call him B):
B - 1,2,4,8,289
A - 2,2,56,2,4,33,4,1,8,
I need to count how many times exist each number from A inside B and sum the result.
For example:
B - 1,2,4,8,289
A - 2,2,56,2,4,33,4,1,8,
result = 1+3+2+1+0
What is elegant way to implement it?
With LINQ it is easy:
int count = A
.Where(x => B.Contains(x))
.Count();
Counts how many times elements from A are contained in B.
As Yuval Itzchakov points out, this can be simplified like this:
int count = A.Count(x => B.Contains(x));
I need to count how many times exist each number from A inside B and sum the result.
You can get both the count and sum as follows
List<int> b = new List<int>() { 1,2,4,8,289 };
List<int> a = new List<int>() { 2,2,56,2,4,33,4,1,8 };
var subset = a.Where(i => b.Contains(i));
var count = subset.Count(); // 7
var sum = subset.Sum(); // 23
Note that I reuse the same Linq expression to get both the count and the sum.
One might be tempted to use a HashSet<int> in place of a List<int> because the .Contains operation is faster. However, HashSet is a set, meaning if the same number is added multiple times, only one copy of that number will remain in the set.
sweet and simple.. one line solution
why dont you try it..
int sum = 0;
A.ToList().ForEach(a=>sum +=B.Count(b=>b==a));
Console.Write(sum);
you can sweap the A/B it will still work
With Linq you can do like this
var B = new List<int>{ 1, 2, 4, 8, 289 };
var A = new List<int> { 2, 2, 56, 2, 4, 33, 4, 1, 8 };
var repetitionSum = B.Select(b => A.Count(a => a == b)).Sum(); //result = 7
And if you want, you can get the individual repetition list like this
var repetition = B.Select(b => A.Count(a => a == b)).ToList();
// { 1, 3, 2, 1, 0 }
It is not clear if you want to know the occurrences of each number or the final count (your text and your example code differ). Here is the code to get the number of appearances of each number
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
int[] a = new []{1,2,3};
int[] b = new []{1,2,2,3};
Dictionary<int, int> aDictionary = a.ToDictionary(i=>i, i => 0);
foreach(int i in b)
{
if(aDictionary.ContainsKey(i))
{
aDictionary[i]++;
}
}
foreach(KeyValuePair<int, int> kvp in aDictionary)
{
Console.WriteLine(kvp.Key + ":" + kvp.Value);
}
}
}

Find Generic List record based on index

I m in a situation where i need to find record from a generic list using its position, means need to find 1st 5th and 9th record , then 2nd 6th and 10th record and so on...
Situation is
A list of projects assigned to a List of Team,
So if we have 20 projects and 4 teams
then 1st project go to 1st team, 2nd go to 2nd team , 3rd go to 3rd team, 4th go to 4th team
then again 5th project go to 1st team
so its like
Projects Team
1 1
2 2
3 3
4 4
5 1
6 2
7 3
8 4
9 1
.
.
so now i want to run a Query on Generic List to get record for each team, so for first team record 1,5 and 9.... need to fetch.
Some thing like
List<Project> lst = list (from Database)
//For 1stTeam
lst = lst.Index(1,5,9...);
//For 2nsTeam
lst = lst.Index(2,6,10...);
Hope i clear my point.
You could do something like this with LINQ Select and GroupBy:
List<int> list = new List<int>{1,2,3,4,5,6,7,8,9,10};
int numberOfTeams = 4;
var projectsByTeam = list
.Select((number, index) => new {Value = number, Index = index})
.GroupBy(item => item.Index % numberOfTeams)
.Select(item => new {TeamNumber = item.Key+1, ProjectIDs = item.Select(x => x.Value).ToList()})
.ToList();
Splits the original list into
{
{TeamNumber = 1, ProjectIDs = {1,5,9}},
{TeamNumber = 2, ProjectIDs = {2,6,10}},
{TeamNumber = 3, ProjectIDs = {3,7}},
{TeamNumber = 4, ProjectIDs = {4,8}},
}
First, this is not specific to generic lists.
You have to create a new list, and then, one by one, add the items from the original list that you want in the new list. You can access single items at a given position via the indexer (square brackets).
List<Project> lst = // list (from Database)
List<Project> firstTeam = new List<Project>();
firstTeam.Add(lst[1]);
firstTeam.Add(lst[5]);
firstTeam.Add(lst[9]);
List<Project> secondTeam = new List<Project>();
secondTeam.Add(lst[2]);
secondTeam.Add(lst[6]);
secondTeam.Add(lst[10]);
Of course, if the items are distributed that regularly throughout the original lst, you can automatically determine the items:
List<Project> firstTeam = new List<Project>();
for (int i = 1; i < lst.Count; i += 4) {
firstTeam.Add(lst[i]);
}
i.e. you loop over the original list, taking every 4th item.
If the items to add to one of the teams are not distributed regularly throughout lst, you will have to add them one by one, but you might be able to make use of the shorter list initializer syntax:
List<Project> firstTeam = new List<Project>() { lst[1], lst[5], lst[9] };
Lastly, note that List<T> starts counting indices at zero, so the very first item is lst[0], not lst[1].
You are looking for the params keyword. It will allow you to pass in to Index an array of arguments, which are the indexes in your case.
In your case an extension method can do the trick:
public static List<Project> Index(this List<Project> list, params int[] indexes)
{
var newList = new List<Project>();
foreach(var index in indexes)
{
newList.Add(list[index]);
}
return newList;
}
// Define other methods and classes here
static IEnumerable<IEnumerable<T>> CustomSplit<T>(this IEnumerable<T> source, int max)
{
var results = new List<List<T>>();
for (int i = 0; i < max; i++)
{
results.Add(new List<T>());
}
int index = 0;
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
int circularIndex = index % max;
results[circularIndex].Add(enumerator.Current);
index++;
}
}
return results;
}
And here is how to use it:
void Main()
{
var elements = Enumerable.Range(0, 100).CustomSplit(4);
}
You can use:
List<Project> projects; // = something from db
var neededIndexes = new[] { 0, 4, 8 };
var result = projects.Where((project, index) => neededIndexes.Contains(index)).ToList();
Or if the indexes are evenly distributed:
List<Project> projects; // = something from db
var result = projects.Where((project, index) => index % 4 == 0).ToList();
This solve your problem:
List for each team:
List<List<Project>> projectsPerTeam = new List<List<Project>> ();
for(int i=0;i<teamsList.Count();i++)
{
projectsPerTeam.Add(new List<Project> ());
}
Now your issue (add project for correct team):
for(int i=0;i<projectsList.Count();i++)
{
projectsPerTeam[i%teamList.Count()].Add(projectsList[i]);
}

Categories

Resources