First found duplicate element using LINQ (Not contiguous) - c#

How do I print the first duplicate elements from an array?
var arr = new int[]{ 3, 2, 5, 1, 5, 4, 2, 15 };
Currently this method print 2 instead of 5.
public int FirstDuplicate(int[] arr)
{
var firstDup = arr
.GroupBy(x => x)
.Where(grp => grp.Count() == 2)
.Select(grp => grp.Key)
.FirstOrDefault();
if (firstDup > 0) return firstDup;
return -1;
}

You can write an extension metod that will return all duplicates from an IEnumerable<T> like
public static class EnumerableExtensions
{
public static IEnumerable<T> Duplicates<T>( this IEnumerable<T> source )
{
var hashset = new HashSet<T>();
foreach ( var item in source )
{
if ( hashset.Contains(item) )
yield return item;
else
hashset.Add(item);
}
}
}
and then use it
var arr = new int[]{ 3, 2, 5, 5, 4, 2, 15 };
var firstDuplicate = arr.Duplicates().First();
see .net fiddle example

This worked for me. I took advantage of comparing values with array indexes, the Distinct() method, and the first element of the resulting array.
var arr = new int[] { 3, 2, 5, 5, 4, 2, 15 };
var adjacentDuplicate = arr.Skip(1) // Skip first
.Where((value,index) => value == arr[index])
.Distinct()
.ToArray(); // Convert to array
if (adjacentDuplicate.Any())
{
Console.WriteLine(adjacentDuplicate[0]); // Print first duplicate
}
else
{
// No duplicates found.
}

Based on Sir Rufo answer, I would make two extensions
public static IEnumerable<T> Duplicates<T>(this IEnumerable<T> source)
{
var hashset = new HashSet<T>();
foreach (var item in source)
{
if (!hashset.Add(item))
{
yield return item;
}
}
}
public static IEnumerable<T?> AsNullable<T>(this IEnumerable<T> source) where T : struct
{
return source.Select(x => (T?)x);
}
You can use it like
var duplicate = arr
.Duplicates()
.AsNullable()
.FirstOrDefault();
The AsNullableconverts int into int? without hard coding the type. When the result is null, there is no duplicity. You can use it in more situations, like calculating the Max of potentially empty sequence of non nullable values (you can define it for IQueryable too). The advantage of this extension is, that when you use it, you know for sure, that null is not valid value in the source. And you would not shoot yourself into the leg when the null suddenly becomes a possible value.

Related

How to select multiple elements into array in Linq query?

Net core application. I have below query in my application
var result = sourceProposal.Quotes
.Where(x=>x.QuotationId == sourceQuoteId)
.FirstOrDefault()
.QuoteLines.Select(x=>(x.Quantity,x.WtgType)).ToArray();
This yields in two array elements such as
0 element 1, "string1"
1 element 2, "string2"
What I am expecting is
(int[] sourceQuantity, string[] destinationTurbineType) = sourceProposal.Quotes
.Where(x=>x.QuotationId == sourceQuoteId)
.FirstOrDefault()
.QuoteLines.Select(x=>(x.Quantity,x.WtgType)).ToArray();
I want to copy to tuple which has int[] sourceQuantity, string[] destinationTurbineType this piece of code is not working and throwing error does not contain definition for destructor and no accessible extension method Descontruct accepting first argument of type int(sourceQuantity, string destinationTurbineType)[].
Can someone help me to copy values to sourceQuantity and destinationTurbineType. Any help would be appreciated. Thanks
Select<TSource,TResult> returns enumerable/queryable of the type returned by selector (IEnumerabe<TResult>/IQueryable <TResult>).
If you want to achieve this with LINQ you can use Aggregate:
// note that sourceQuantity and destinationTurbineType would be lists, not arrays
var (sourceQuantity, destinationTurbineType) = sourceProposal.Quotes
.Where(x=>x.QuotationId == sourceQuoteId)
.FirstOrDefault()
.QuoteLines
.Aggregate((ints: new List<int>(), strs: new List<string>()), (aggr, curr) =>
{
aggr.ints.Add(curr.Quantity);
aggr.strs.Add(curr.WtgType);
return aggr;
});
Or just use simple for loop and copy data to destination arrays (possibly move to some extension method). Something along this lines:
var quoteLines = sourceProposal.Quotes
.Where(x=>x.QuotationId == sourceQuoteId)
.FirstOrDefault()
.QuoteLines; // assuming it is materialized collection with indexer like an array or list
int[] sourceQuantity = new int[quoteLines.Length]; // or Count
string[] destinationTurbineType = new string[quoteLines.Count()];
for(int i = 0; i < quoteLines.Length; i++)
{
var curr = quoteLines[i];
sourceQuantity[i] = curr.Quantity;
destinationTurbineType[i] = curr.WtgType;
}
Currently there is no built-in LINQ method to do this. But you could write your own extension method. Something like the following:
public static class EnumerableExtensions
{
public static (TFirst[] xs, TSecond[] ys) Unzip<TFirst, TSecond>(this IEnumerable<(TFirst, TSecond)> zipped)
{
var xs = new List<TFirst>();
var ys = new List<TSecond>();
foreach (var (x, y) in zipped)
{
xs.Add(x);
ys.Add(y);
}
return (xs.ToArray(), ys.ToArray());
}
}
var (xs, ys) =
new[] { 1, 2, 3 }
.Zip(new[] { "a", "b", "c" })
.Unzip();
Console.WriteLine(string.Join(", ", xs)); // 1, 2, 3
Console.WriteLine(string.Join(", ", ys)); // a, b, c
Or in the case of your example, you could then use:
(int[] sourceQuantity, string[] destinationTurbineType) = sourceProposal.Quotes
.Where(x=>x.QuotationId == sourceQuoteId)
.FirstOrDefault()
.QuoteLines.Select(x=>(x.Quantity,x.WtgType)).Unzip();

Split Array By Values In Sequence [duplicate]

This question already has answers here:
LINQ to find series of consecutive numbers
(6 answers)
Closed 5 years ago.
Is there an easy (linq?) way to split an int array into new arrays based off unbroken numerical sequences? For example given this pseudo code:
[Fact]
public void ArraySpike()
{
var source = new[] {1, 2, 3, 7, 8, 9, 12, 13, 24};
var results = SplitArray(source);
Assert.True(results[0] == new[] {1, 2, 3});
Assert.True(results[1] == new[] {7, 8, 9});
Assert.True(results[2] == new[] {12, 13});
Assert.True(results[3] == new[] {24});
}
public int[][] SplitArray(int[] source)
{
return source.???
}
This can work with the linq extension Aggregate. My seeding is not very elegant but that is easy enough to change. The results variable will contain the array of arrays and they are actually of type List<T> because that way they can be easily grown in the function where an array [] is always of fixed size.
This also assumes the source is already ordered and unique, if that is not the case add .OrderBy(x => x).Distinct()
var source = new[] { 1, 2, 3, 7, 8, 9, 12, 13, 24 };
var results = new List<List<int>>{new List<int>()};
var temp = source.Aggregate(results[0], (b, c) =>
{
if (b.Count > 0 && b.Last() != c - 1)
{
b = new List<int>();
results.Add(b);
}
b.Add(c);
return b;
});
I dug up this extension method from my personal collection:
public static IEnumerable<IEnumerable<T>> GroupConnected<T>(this IEnumerable<T> list, Func<T,T,bool> connectionCondition)
{
if (list == null)
{
yield break;
}
using (var enumerator = list.GetEnumerator())
{
T prev = default(T);
var temp = new List<T>();
while (enumerator.MoveNext())
{
T curr = enumerator.Current;
{
if(!prev.Equals(default(T)) && !connectionCondition(prev, curr))
{
yield return temp;
temp = new List<T>();
}
temp.Add(curr);
}
prev = curr;
}
yield return temp;
}
}
It solves the problem in a more general sense: split up a sequence in subsequences of elements that are "connected" somehow. It traverses the sequence and collects each element in a temporary list until the next item isn't "connected". It then returns the temporary list and begins a new one.
Your array elements are connected when they have a difference of 1:
var results = source.GroupConnected((a,b) => b - a == 1);

Move list elements meeting condition to the top of the list

I want to move specific number to the top of this list.
int numberToBeMovedOnTop = 4;
List<int> lst = new List<int>(){1, 2, 3, 4, 5, 5, 4, 7, 9, 4, 2, 1};
List<int> lstOdd = lst.FindAll(l => l == numberToBeMovedOnTop);
lstOdd.AddRange(lst.FindAll(l => l != numberToBeMovedOnTop));
Where numberToBeMovedOnTop is a variable.
This gives me the desired result but is a better solution for this? I can iterate the list once and swap first occurence of numberToBeMovedOnTop with first element, second occurence with numberToBeMovedOnTop with second element and so on. But can this be done with some built-in C# function without iterating the list twice?
You could use LINQ:
List<int> lstOdd = lst.OrderByDescending(i => i == numberToBeMovedOnTop).ToList();
Why OrderByDescending? Because the comparison returns a bool and true is higher than false. You could also use:
List<int> lstOdd = lst.OrderBy(i => i == numberToBeMovedOnTop ? 0 : 1).ToList();
Note that this works because OrderBy and OrderByDescending are performing a stable sort. That means that the original order remains for all equal items.
For what it's worth, here is an extension method that works with any type and predicate and is a little bit more efficient:
public static List<T> PrependAll<T>(this List<T> list, Func<T, bool> predicate)
{
var returnList = new List<T>();
var listNonMatch = new List<T>();
foreach (T item in list)
{
if (predicate(item))
returnList.Add(item);
else
listNonMatch.Add(item);
}
returnList.AddRange(listNonMatch);
return returnList;
}
Usage: List<int> lstOdd = lst.PrependAll(i => i == numberToBeMovedOnTop);
Aside from using linq, it might be just as efficient/understandable to do this without linq
var listToAdd = new List<int>();
var listOdd = new List<int>();
for(int i = 0; i < lst.Count; i++)
{
if(lst[i] == numberToBeMovedOnTop)
{
listToAdd.Add(numberToBeMovedOnTop);
}
else
{
listOdd.Add(lst[i]);
}
}
listOdd.AddRange(listToAdd);
Keep track of those that you've removed, then add them on afterwards
Group by the predicate, then union?
var nums = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var grp = nums.GroupBy(x => x % 2 == 0).ToList();
var changed = grp[0].Union(grp[1]).ToList();

Splitting an array using LINQ

I have a collection uni-dimensional like this:
[1,2,4,5.....n]
I would like to convert that collection in a bi-dimensional collection like this:
[[1,2,3],
[4,5,6],
...]
Basically I want to group or split if you want, the array in groups of 'n' members
I can do it with a foreach statement, but I am currently learning LINQ so instead of iterating through all elements and create a new array manually I would like to use the LINQ features (if applicable)
Is there any LINQ function to help me to accomplish this??
I was thinking in the GroupBy or SelectMany I do not know if they will help me though but they might
Any help will be truly appreciate it =) :**
You can group by the index divided by the batch size, like this:
var batchSize = 3;
var batched = orig
.Select((Value, Index) => new {Value, Index})
.GroupBy(p => p.Index/batchSize)
.Select(g => g.Select(p => p.Value).ToList());
Use MoreLinq.Batch
var result = inputArray.Batch(n); // n -> batch size
Example
var inputs = Enumerable.Range(1,10);
var output = inputs.Batch(3);
var outputAsArray = inputs.Batch(3).Select(x=>x.ToArray()).ToArray(); //If require as array
You want Take() and Skip(). These methods will let you split an IEnumerable. Then you can use Concat() to slap them together again.
The sample below will split an array into groups of 4 items each.
int[] items = Enumerable.Range(1, 20).ToArray(); // Generate a test array to split
int[][] groupedItems = items
.Select((item, index) => index % 4 == 0 ? items.Skip(index).Take(4).ToArray() : null)
.Where(group => group != null)
.ToArray();
It's not a pure LINQ but it's intended to be used with it:
public static class MyEnumerableExtensions
{
public static IEnumerable<T[]> Split<T>(this IEnumerable<T> source, int size)
{
if (source == null)
{
throw new ArgumentNullException("source can't be null.");
}
if (size == 0)
{
throw new ArgumentOutOfRangeException("Chunk size can't be 0.");
}
List<T> result = new List<T>(size);
foreach (T x in source)
{
result.Add(x);
if (result.Count == size)
{
yield return result.ToArray();
result = new List<T>(size);
}
}
}
}
It can be used from your code as:
private void Test()
{
// Here's your original sequence
IEnumerable<int> seq = new[] { 1, 2, 3, 4, 5, 6 };
// Here's the result of splitting into chunks of some length
// (here's the chunks length equals 3).
// You can manipulate with this sequence further,
// like filtering or joining e.t.c.
var splitted = seq.Split(3);
}
It's as simple as:
static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> ToPages<T>(this IEnumerable<T> elements, int pageSize)
{
if (elements == null)
throw new ArgumentNullException("elements");
if (pageSize <= 0)
throw new ArgumentOutOfRangeException("pageSize","Must be greater than 0!");
int i = 0;
var paged = elements.GroupBy(p => i++ / pageSize);
return paged;
}
}
I based my solution of Jeremy Holovacs's answer and used Take() and Skip() to create subarrays.
const int batchSize = 3;
int[] array = new int[] { 1,2,4,5.....n};
var subArrays = from index in Enumerable.Range(0, array.Length / batchSize + 1)
select array.Skip(index * batchSize).Take(batchSize);
Starting with .NET 6, there is the System.Linq.Enumerable.Chunk(this IEnumerable<TSource>, int size) extension method. It returns an IEnumerable<TSource[]> where each item is an array of size elements, except the last item, which could have fewer.
Code like this:
using System;
using System.Collections.Generic;
using System.Linq;
int[] input = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<int[]> chunks = input.Chunk(3);
foreach (int[] chunk in chunks)
{
foreach (int i in chunk)
{
Console.Write($"{i} ");
}
Console.WriteLine();
}
outputs
1 2 3
4 5 6
7 8 9
10

Selecting unique elements from a List in C#

How do I select the unique elements from the list {0, 1, 2, 2, 2, 3, 4, 4, 5} so that I get {0, 1, 3, 5}, effectively removing all instances of the repeated elements {2, 4}?
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
var uniqueNumbers =
from n in numbers
group n by n into nGroup
where nGroup.Count() == 1
select nGroup.Key;
// { 0, 1, 3, 5 }
var nums = new int{ 0...4,4,5};
var distinct = nums.Distinct();
make sure you're using Linq and .NET framework 3.5.
With lambda..
var all = new[] {0,1,1,2,3,4,4,4,5,6,7,8,8}.ToList();
var unique = all.GroupBy(i => i).Where(i => i.Count() == 1).Select(i=>i.Key);
C# 2.0 solution:
static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
Dictionary<T, int> counts = new Dictionary<T, int>();
foreach (T item in things)
{
int count;
if (counts.TryGetValue(item, out count))
counts[item] = ++count;
else
counts.Add(item, 1);
}
foreach (KeyValuePair<T, int> kvp in counts)
{
if (kvp.Value == 1)
yield return kvp.Key;
}
}
Here is another way that works if you have complex type objects in your List and want to get the unique values of a property:
var uniqueValues= myItems.Select(k => k.MyProperty)
.GroupBy(g => g)
.Where(c => c.Count() == 1)
.Select(k => k.Key)
.ToList();
Or to get distinct values:
var distinctValues = myItems.Select(p => p.MyProperty)
.Distinct()
.ToList();
If your property is also a complex type you can create a custom comparer for the Distinct(), such as Distinct(OrderComparer), where OrderComparer could look like:
public class OrderComparer : IEqualityComparer<Order>
{
public bool Equals(Order o1, Order o2)
{
return o1.OrderID == o2.OrderID;
}
public int GetHashCode(Order obj)
{
return obj.OrderID.GetHashCode();
}
}
If Linq isn't available to you because you have to support legacy code that can't be upgraded, then declare a Dictionary, where the first int is the number and the second int is the number of occurences. Loop through your List, loading up your Dictionary. When you're done, loop through your Dictionary selecting only those elements where the number of occurences is 1.
I believe Matt meant to say:
static IEnumerable<T> GetUniques<T>(IEnumerable<T> things)
{
Dictionary<T, bool> uniques = new Dictionary<T, bool>();
foreach (T item in things)
{
if (!(uniques.ContainsKey(item)))
{
uniques.Add(item, true);
}
}
return uniques.Keys;
}
There are many ways to skin a cat, but HashSet seems made for the task here.
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
HashSet<int> r = new HashSet<int>(numbers);
foreach( int i in r ) {
Console.Write( "{0} ", i );
}
The output:
0 1 2 3 4 5
Here's a solution with no LINQ:
var numbers = new[] { 0, 1, 2, 2, 2, 3, 4, 4, 5 };
// This assumes the numbers are sorted
var noRepeats = new List<int>();
int temp = numbers[0]; // Or .First() if using IEnumerable
var count = 1;
for(int i = 1; i < numbers.Length; i++) // Or foreach (var n in numbers.Skip(1)) if using IEnumerable
{
if (numbers[i] == temp) count++;
else
{
if(count == 1) noRepeats.Add(temp);
temp = numbers[i];
count = 1;
}
}
if(count == 1) noRepeats.Add(temp);
Console.WriteLine($"[{string.Join(separator: ",", values: numbers)}] -> [{string.Join(separator: ",", values: noRepeats)}]");
This prints:
[0,1,2,2,2,3,4,4,5] -> [0,1,3,5]
In .Net 2.0 I`m pretty sure about this solution:
public IEnumerable<T> Distinct<T>(IEnumerable<T> source)
{
List<T> uniques = new List<T>();
foreach (T item in source)
{
if (!uniques.Contains(item)) uniques.Add(item);
}
return uniques;
}

Categories