How to remove duplicate pairs in a List - c#

I got a List with pairs of integers. How do I remove pairs if they're duplicates? Distinct wont work cause the pair could be (2, 1) instead of (1, 2).
My list looks like this:
1, 2
2, 3
3, 1
3, 2
2, 4
4, 3
... I don't need (2, 3) and (3, 2)
I made a public struct FaceLine with public int A and B, then var faceline = new List<FaceLine>();.
I'm new to C# and lost.

You could use a custom IEqualityComparer<FaceLine>:
public class UnorderedFacelineComparer : IEqualityComparer<FaceLine>
{
public bool Equals(FaceLine x, FaceLine y)
{
int x1 = Math.Min(x.A, x.B);
int x2 = Math.Max(x.A, x.B);
int y1 = Math.Min(y.A, y.B);
int y2 = Math.Max(y.A, y.B);
return x1 == y1 && x2 == y2;
}
public int GetHashCode(FaceLine obj)
{
return obj.A ^ obj.B;
}
}
Then the query was very simple:
var comparer = new UnorderedFacelineComparer();
List<FaceLine> nonDupList = faceLine
.GroupBy(fl => fl, comparer)
.Where(g => g.Count() == 1)
.Select(g => g.First())
.ToList();
If you wanted to keep one of the duplicates you just need to remove the Where:
List<FaceLine> nonDupList = faceLine
.GroupBy(fl => fl, comparer)
.Select(g => g.First())
.ToList();

If you're happy using the common DistinctBy Linq extension (available via NuGet) you can do this fairly simply like so:
var result = list.DistinctBy(x => (x.A > x.B) ? (x.A, x.B) : (x.B, x.A));
Sample console app:
using System;
using System.Collections.Generic;
using MoreLinq;
namespace Demo
{
class Test
{
public Test(int a, int b)
{
A = a;
B = b;
}
public readonly int A;
public readonly int B;
public override string ToString()
{
return $"A={A}, B={B}";
}
}
class Program
{
static void Main()
{
var list = new List<Test>
{
new Test(1, 2),
new Test(2, 3),
new Test(3, 1),
new Test(3, 2),
new Test(2, 4),
new Test(4, 3)
};
var result = list.DistinctBy(x => (x.A > x.B) ? (x.A, x.B) : (x.B, x.A));
foreach (var item in result)
Console.WriteLine(item);
}
}
}

Using Linq :
List<List<int>> data = new List<List<int>>() {
new List<int>() {1, 2},
new List<int>() {2, 3},
new List<int>() {3, 1},
new List<int>() {3, 2},
new List<int>() {2, 4},
new List<int>() {4, 3}
};
List<List<int>> results =
data.Select(x => (x.First() < x.Last())
? new { first = x.First(), last = x.Last() }
: new { first = x.Last(), last = x.First() })
.GroupBy(x => x)
.Select(x => new List<int>() { x.First().first, x.First().last }).ToList();

Form a set of sets and you get the functionality for free (each smaller set contains exactly two integers).

Related

Add index position Value of an array of array using Linq

We can do sum using arr.Sum() function. But if it is an array of arrays. How will we add all values.
suppose data is
Array/List is [[1,2,3],[3,4,5],[5,4,3]]
how will you get s1 , sum of all first index value, s2 , sum of second index value and so on using LINQ.
If you want to sum up columns' values with a help of Linq:
int[][] source = new int[][] {
new int[] { 1, 2, 3},
new int[] { 3, 4, 5},
new int[] { 5, 4, 3},
};
int maxCol = source.Max(item => item.Length);
var colsSum = Enumerable
.Range(0, maxCol)
.Select(index => source.Sum(item => item.Length > index ? item[index] : 0))
.ToArray(); // let's meaterialize into an array
Test:
Console.Write(string.Join(", ", colsSum));
Outcome:
9, 10, 11
Summing up lines' values is easier:
// [6, 12, 12]
var linesSum = source
.Select(item => item.Sum())
.ToArray();
If you want total sum:
// 30
var total = source
.Select(item => item.Sum())
.Sum();
or
// 30
var total = source
.SelectMany(item => item)
.Sum();
Use combination of Aggregate and Zip
var arrays = new[]
{
new[] { 1, 2, 3 },
new[] { 3, 4, 5 },
new[] { 5, 4, 3 }
};
var result =
arrays.Aggregate(Enumerable.Repeat(0, 3),
(total, array) => total.Zip(array, (sum, current) => sum + current));
// result = { 9, 10, 11 }
Enumerable<T>.Zip executes provided function with items of same index.
A possible LINQ based approach (which will handle variable number of columns in each row):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public class Program
{
private static IEnumerable<int> GetTotalsPerColumn(int[][] inputData)
{
var data = inputData.SelectMany(z =>
{
return z.Select((item, index) => new { item, index });
})
.GroupBy(z => z.index)
.OrderBy(z => z.Key)
.Select(y => y.Select(z => z.item).Sum()
);
return data;
}
static void Main(string[] args)
{
var inputData = new[] {
new[] { 1, 2, 3, 5},
new[] { 3, 4, 5, 6},
new[] { 5, 4, 3},
};
var values = GetTotalsPerColumn(inputData);
foreach (var value in values)
{
Console.WriteLine(value);
}
Console.ReadLine();
}
}
}
If you are happy to avoid LINQ, this is another approach you could consider.
GetTotalsPerColumn populates a Dictionary where the key is the column number, and the value is the sum.
using System;
using System.Collections.Generic;
namespace Test
{
public class Program
{
static void Main(string[] args)
{
var inputData = new[] {
new[] { 1, 2, 3, 5},
new[] { 3, 4, 5, 6},
new[] { 5, 4, 3},
};
var values = GetTotalsPerColumn(inputData);
foreach (var value in values)
{
Console.WriteLine(value.Key + " - " + value.Value);
}
Console.ReadLine();
}
private static Dictionary<int, int> GetTotalsPerColumn(int[][] inputData)
{
var values = new Dictionary<int, int>();
foreach (var line in inputData)
{
for (int i = 0; i < line.Length; i++)
{
int tempValue;
values.TryGetValue(i, out tempValue);
tempValue += line[i];
values[i] = tempValue;
}
}
return values;
}
}
}

How to count list items grouped by an embedded list

I have a class like
class MyClass
{
public DateTime Date { get; set; }
public List<int> IdList { get; set; }
public MyClass(DateTime initDate)
{
Date = initDate;
IdList = new List<int>();
}
}
and need to count the number of entries in a List<MyClass>, grouped by each int in IdList.
I have experimented with various Linq constructs, but I cannot get anything to work. Here is what I have so far:
List<MyClass> myc = new List<MyClass>();
myc.Add(new MyClass(new DateTime(2016, 1, 1)) { IdList = new List<int> { 1, 2 } });
myc.Add(new MyClass(new DateTime(2016, 1, 2)) { IdList = new List<int> { 1, 3 } });
myc.Add(new MyClass(new DateTime(2016, 1, 3)) { IdList = new List<int> { 1, 4 } });
myc.Add(new MyClass(new DateTime(2016, 1, 4)) { IdList = new List<int> { 5, 6 } });
myc.Add(new MyClass(new DateTime(2016, 1, 5)) { IdList = new List<int> { 2, 3 } });
var grouped = from p in myc
group p by p.IdList into g
select new { Id = g.Key, Count = g.Count() };
foreach (var x in grouped)
{
Console.WriteLine("ID: {0}, Count: {1}", x.Id, x.Count);
}
// Expecting output like:
// ID: 1, Count: 3
// ID: 2, Count : 2
// etc.
If there was a single int Id property in MyClass, it would be straightforward, but I cannot work out how to use the List<int>. Is there any alternative to writing nested loops and populating a Dictionary? Thanks for any help.
You can use SelectMany
var grouped = myc.SelectMany(x => x.IdList).GroupBy(x => x);
foreach (var i in g)
{
Console.WriteLine(string.Format("Id: {0}, Count: {1}", i.Key,i.Count()));
}
This should give you the output you're looking for.
I don't know if I've understand your requeriment correctly. But try this and let me know:
var groupedIds = myc.SelectMany(x => x.IdList.Select(i => i))
.GroupBy(x => x)
.ToList();
The full fiddle here
And here SelectMany documentation so you know what this code means.
Hope this helps!

How do you transpose dimensions in a 2D collection using LINQ?

Consider the following structure:
IEnumerable<IEnumerable<int>> collection = new[] {
new [] {1, 2, 3},
new [] {4, 5, 6},
new [] {7, 8, 9}
};
How can I enumerate this collection so that I obtain IEnumerable<int> collections made up of the first items, second items, etc.?
That is, {1, 4, 7}, {2, 5, 8}, ...
(Though the implementation I've chosen is int[] objects, assume you only have IEnumerable<int> functionality. Thanks.)
Here's an approach that uses a generator instead of recursion. There's less array construction too, so it might be faster, but that's totally conjecture.
public static IEnumerable<IEnumerable<T>> Transpose<T>(
this IEnumerable<IEnumerable<T>> #this)
{
var enumerators = #this.Select(t => t.GetEnumerator())
.Where(e => e.MoveNext());
while (enumerators.Any()) {
yield return enumerators.Select(e => e.Current);
enumerators = enumerators.Where(e => e.MoveNext());
}
}
Just my 2 cents
In pure linq:
var transpond = collection.First().Select((frow,i)=>collection.Select(row=>row.ElementAt(i)));
Or with some inpurity:
var r1 = collection.First().Select((frow, i) => collection.Select(row => row.ToArray()[i]));
Code credit goes here (untested but looks fine).
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> values)
{
if (!values.Any())
return values;
if (!values.First().Any())
return Transpose(values.Skip(1));
var x = values.First().First();
var xs = values.First().Skip(1);
var xss = values.Skip(1);
return
new[] {new[] {x}
.Concat(xss.Select(ht => ht.First()))}
.Concat(new[] { xs }
.Concat(xss.Select(ht => ht.Skip(1)))
.Transpose());
}
}
//Input: transpose [[1,2,3],[4,5,6],[7,8,9]]
//Output: [[1,4,7],[2,5,8],[3,6,9]]
var result = new[] {new[] {1, 2, 3}, new[] {4, 5, 6}, new[] {7, 8, 9}}.Transpose();
Assuming all the sequences are of the same length.
static void Main(string[] args)
{
IEnumerable<IEnumerable<int>> collection =
new[]
{
new [] {1, 2, 3},
new [] {4, 5, 6 },
new [] {7, 8, 9}
};
Console.WriteLine("\tInitial");
Print(collection);
var transposed =
Enumerable.Range(0, collection.First().Count())
.Select(i => collection.Select(j => j.ElementAt(i)));
Console.WriteLine("\tTransposed");
Print(transposed);
}
static void Print<T>(IEnumerable<IEnumerable<T>> collection)=>
Console.WriteLine(string.Join(Environment.NewLine, collection.Select(i => string.Join(" ", i))));
Gives:
Initial
1 2 3
4 5 6
7 8 9
Transposed
1 4 7
2 5 8
3 6 9
If all elements are guaranteed to be the same length, you could do this:
IEnumerable<IEnumerable<int>> Transpose(IEnumerable<IEnumerable<int>> collection)
{
var width = collection.First().Count();
var flattened = collection.SelectMany(c => c).ToArray();
var height = flattened.Length / width;
var result = new int[width][];
for (int i = 0; i < width; i++)
{
result[i] = new int[height];
for (int j = i, k = 0; j < flattened.Length; j += width, k++)
result[i][k] = flattened[j];
}
return result;
}

name of assoc-diff in C#

What do you call this method, (is it available in .net?)
var list1 = new List<int>() { 1, 2, 2, 3, 4 };
var list2 = new List<int>() { 1, 2, 3};
var results = list1.diff(list2);
results:
{ 2, 4 }
The closest thing built in is the Except LINQ operator.
Produces the set difference of two sequences.
Though with your example it will result in:
{ 4 }
I don't believe there is a direct analogue to what you want.
You actually need a multiset implementation. Although there is no multiset out of the box in BCL, there are some ideas here and in the linked question.
Or you can actually implement one by yourself, it's not so complicated:
class Multiset<K> // maybe implement IEnumerable?
{
Dictionary<K, int> arities = new Dictionary<K, int>();
...
Multiset<K> Except(Multiset<K> other)
{
foreach (var k in arities.keys)
{
int arity = arities[k];
if (other.Contains(k))
arity -= other.Arity(k);
if (arity > 0)
result.Add(k, arity);
}
return result;
}
}
This exactly return what you want, You can refactor it in a Extension Method:
var results = list1.GroupBy(p => p).Select(p => new { item = p.Key, count = p.Count() })
.Concat(list2.GroupBy(p => p).Select(p => new { item = p.Key, count = -p.Count() }))
.GroupBy(p => p.item).Select(p => new { item = p.Key, count = p.Sum(q => q.count) })
.Where(p => p.count > 0)
.SelectMany(p => Enumerable.Repeat(p.item, p.count));
Like this: (see oded's post for a linq to msdn)
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);

C# - How to get complement of two lists without identifiers

I have two lists of the same type. That type does not have an identifier or any other guaranteed way to programatically distinguish.
List A: {1, 2, 2, 3, 5, 8, 8, 8}
List B: {1, 3, 5, 8}
I want the items from A that are not in B.
Desired Result: {2, 2, 8, 8}
If the types had identifiers, I could use a statement like the following...
var result = listA
.Where(a => listB.Where(b => b.Id == a.Id).Count() == 0)
.ToList();
So far, the only way I can do this is with a loop where I add each item the number of times it doesn't appear in the original list.
foreach (var val in listB.Select(b => b.val).Distinct())
{
var countA = listA.Where(a => a.val == val).Count();
var countB = listB.Where(b => b.val == val).Count();
var item = listA.Where(a => a.val == val).FirstOrDefault();
for (int i=0; i<countA-countB; i++)
result.Add(item);
}
Is there a cleaner way to achieve this?
EDIT:
Here is a simplified version of the object in the lists. It's coming from a Web service that's hitting another system.
public class myObject
{
public DateTime SomeDate { get; set; }
public decimal SomeNumber; { get; set; }
public bool IsSomething { get; set; }
public string SomeString { get; set; }
}
The data I am receiving has the same values for SomeDate/SomeString and repeated values for SomeNumber and IsSomething. Two objects might have equal properties, but I need to treat them as distinct objects.
try this:
var listA = new List<Int32> {1, 2, 2, 3, 5, 8, 8, 8};
var listB = new List<Int32> {1, 3, 5, 8};
var listResult = new List<Int32>(listA);
foreach(var itemB in listB)
{
listResult.Remove(itemB);
}
What am I missing?
class Program
{
static void Main(string[] args)
{
List<int> a = new List<int>();
a.Add(1);
a.Add(2);
a.Add(2);
a.Add(3);
a.Add(5);
a.Add(8);
a.Add(8);
a.Add(8);
List<int> b = new List<int>();
b.Add(1);
b.Add(3);
b.Add(5);
b.Add(8);
foreach (int x in b)
a.Remove(x);
foreach (int x in a)
Console.WriteLine(x);
Console.ReadKey(false);
}
}
Are the objects same instances in both lists? If so you can use .Where(a => listB.Where(b => b == a).Count() == 0)
Or
.Where(a => !listB.Any(b => b == a))
You could sort both lists and then iterate through them both at the same time.
public IEnumerable<int> GetComplement(IEnumerable<int> a, IEnumerable<int> b)
{
var listA = a.ToList();
listA.Sort();
var listB = b.ToList();
listB.Sort();
int i=0,j=0;
while( i < listA.Count && j < listB.Count )
{
if(listA[i] > listB[j]) {yield return listB[j];j++;}
else if (listA[i] < listB[j]) {yield return listA[i]; i++; }
else {i++;j++;}
}
while(i < listA.Count)
{
yield return listA[i];
i++;
}
while(j < listB.Count)
{
yield return listB[j];
j++;
}
}
I don't know if this is "cleaner", but it should be more performant on large sets of data.
This is a bit nasty but it does what you want. Not sure about performance though.
var a = new List<int> { 1, 2, 2, 3, 5, 8, 8, 8 };
var b = new List<int> { 1, 3, 5, 8 };
var c = from x in a.Distinct()
let a_count = a.Count(el => el == x)
let b_count = b.Count(el => el == x)
from val in Enumerable.Repeat (x, a_count - b_count)
select val;
Why don't you implement your own equality comparer for your myObject:
public class YourTypeEqualityComparer : IEqualityComparer<myObject>
{
public bool Equals(myObject x, myObject y)
public int GetHashCode(myObject obj)
}
and then use it like this:
var list1 = new List<myObj>();
var list2 = new List<myObj>()
list1.RemoveAll(i =>
list2.Contains(list1),
new YourTypeEqualityComparer()
);
now list1 contains result.

Categories