I have an ordered list of entities. Each entity has an int UniqueKey property.
I want the list to go through a transformation whereby the UniqueKey values are made unique (assuming that there are duplicates). This is done by finding duplicates and progressively incrementing them.
Step-by-step Process:
Start at index 1 (I am using zero-based indexing)
If any previous elements have the same UniqueId value, increment the value at the current index.
Repeat (2) until no previous elements have the same UniqueId
Move one element to the right
For example, { 1, 1, 1, 3, 3, 8 } would go through the following steps:
{ 1, 2, 1, 3, 3, 8 } : Index 1 incremented
{ 1, 2, 2, 3, 3, 8 } : Index 2 incremented
{ 1, 2, 3, 3, 3, 8 } : Index 2 incremented again
{ 1, 2, 3, 4, 3, 8 } : Index 3 incremented
{ 1, 2, 3, 4, 4, 8 } : Index 4 incremented
{ 1, 2, 3, 4, 5, 8 } : Index 4 incremented again
The code below performs the above-mentioned algorithm in a very procedural manner:
entities = entities.OrderBy(x => x.UniqueId);
foreach (var entity in entities)
{
var leftList = entities.Take(entities.IndexOf(entity));
while (leftList.Any(x => x.UniqueId == entity.UniqueId))
{
entity.UniqueId++;
}
}
Question: Is it possible to implement this in LINQ?
You algorithm could be simplify a lot. Just iterate over, and if an Id is lower than the previous, increment it by one. No Linq, no O(n^2), just O(n):
{ 1, 2, 1, 3, 3, 8 } : Index 1 incremented
{ 1, 2, 3, 3, 3, 8 } : Index 2 incremented
{ 1, 2, 3, 4, 3, 8 } : Index 3 incremented
{ 1, 2, 3, 4, 5, 8 } : Index 4 incremented
entities = entities.OrderBy(x => x.UniqueId).ToList();
for(int index = 1; index < entities.Count; index++)
{
int previous = entities[index - 1].UniqueId;
if (previous >= entities[index].UniqueId)
{
entities[index].UniqueId = previous + 1;
}
}
Technically yes:
var indexedEntities =
entities.Select((e, i) => new { Entity = e, Index = i })
.ToList();
indexedEntities.ForEach(ie =>
ie.Entity.UniqueId =
indexedEntities.Any(prev => prev.Index < ie.Index)
&& ie.Entity.UniqueId
<= indexedEntities.TakeWhile(prev => prev.Index < ie.Index)
.Max(prev => prev.Entity.UniqueId)
? indexedEntities.TakeWhile(prev => prev.Index < ie.Index)
.Max(prev => prev.Entity.UniqueId) + 1
: ie.Entity.UniqueId);
var result = indexedEntities.Select(ie => ie.Entity);
Though please, for the love of everything sacred in IT, don't, just don't :)
This doesn't follow your algorithm faithfully, but it might give you the result you want.
Essentially compare each element with the next, and increment the latter's Id to be one more than the former's.
entities.OrderBy(e => e.Id)
.Aggregate((e1, e2) => { if (e1.Id >= e2.Id) { e2.Id = e1.Id + 1; } return e2; });
If you are desperate for a linq solution why not just use the index as the id.
entities.OrderBy(x => x.UniqueId).Select((x,i) => {
x.UniqueId = i;
return x;
}).ToArray();
Related
I am trying to get the most frequent values in an array using LINQ in C#.
For example,
int[] input = {1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8};
output = {1, 6}
int[] input = {1, 2, 2, 3 ,3, 3, 5}
output = {3}
Please let me know how to build LINQ.
Please read be careful.
This is a different problem with Select most frequent value using LINQ
I have to choose only the most frequent values. The code below is similar, but I can't use Take(5) because I don't know the number of results.
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
IEnumerable<int> top5 = nums
.GroupBy(i => i)
.OrderByDescending(g => g.Count())
.Take(5)
.Select(g => g.Key);
this output is {1, 2, 3, 4, 5}
but my expected output = {1, 2}
Please read the questions carefully and answer.
Thanks and regards.
Just to add to the plethora of answers:
int[] input = { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
var result = input
.GroupBy(i => i)
.GroupBy(g => g.Count())
.OrderByDescending(g => g.Key)
.First()
.Select(g => g.Key)
.ToArray();
Console.WriteLine(string.Join(", ", result)); // Prints "1, 6"
[EDIT]
In case anyone finds this interesting, I compared the performance of the above between .net 4.8 and .net 5.0 as follows:
(1) Added a Comparer class to instrument the number of comparisons made:
class Comparer : IComparer<int>
{
public int Compare(int x, int y)
{
Console.WriteLine($"Comparing {x} with {y}");
return x.CompareTo(y);
}
}
(2) Modified the call to OrderByDescending() to pass a Comparer:
.OrderByDescending(g => g.Key, new Comparer())
(3) Multi-targeted my test console app to "net48" and "net5.0".
After making those changes the output was as follows:
For .net 4.8:
Comparing 1 with 3
Comparing 1 with 1
Comparing 1 with 2
Comparing 3 with 3
Comparing 3 with 2
Comparing 3 with 3
1, 6
For .net 5.0:
Comparing 3 with 1
Comparing 3 with 2
1, 6
As you can see, .net 5.0 is better optimised. For .net Framework however, (as /u/mjwills mentions below) it would likely be more performant to use a MaxBy() extension to avoid having to use OrderByDescending() - but only if instrumentation indicates that the sort is causing a performance issue.
If you want to do it in pure LINQ in one query you can group groups by count and select the max one:
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
var tops = nums
.GroupBy(i => i)
.GroupBy(grouping => grouping.Count())
.OrderByDescending(gr => gr.Key)
.Take(1)
.SelectMany(g => g.Select(g => g.Key))
.ToList();
Note that it is not a most effective and clear solution.
UPD
A little bit more effective version using Aggregate to perform MaxBy. Note that it will fail for empty collections unlike the previous one:
var tops = nums
.GroupBy(i => i)
.GroupBy(grouping => grouping.Count())
.Aggregate((max, curr) => curr.Key > max.Key ? curr : max)
.Select(gr => gr.Key);
Also you can use MaxBy from MoreLinq or one introduced in .NET 6.
You can store your result in an IEnumerable of tuples with the first item being the number, the second item being the count of the number in your input array. Then you look at the count of your group with most elements, and take all the tuples where the second items equals your maximum.
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
var intermediate = nums
.GroupBy(i => i)
.Select(g => (g.Key,g.Count()));
int amount = intermediate.Max(x => x.Item2);
IEnumerable<int> mostFrequent = intermediate
.Where(x => x.Item2 == amount)
.Select(x => x.Item1);
Online demo: https://dotnetfiddle.net/YCVGam
Use a variable to capture the number of items for the first item, then use TakeWhile to get all the groups with that number of items.
void Main()
{
var input = new[] { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
int numberOfItems = 0;
var output = input
.GroupBy(i => i)
.OrderByDescending(group => group.Count());
var maxNumberOfItems = output.FirstOrDefault()?.Count() ?? 0;
var finalOutput = output.TakeWhile(group => group.Count() == maxNumberOfItems).ToList();
foreach (var item in finalOutput)
{
Console.WriteLine($"Value {item.Key} has {item.Count()} members");
}
}
You can do this as a single query as well:
int? numberOfItems = null;
var finalOutput = input
.GroupBy(i => i)
.OrderByDescending(group => group.Count())
.TakeWhile(i =>
{
var count = i.Count();
numberOfItems ??= count;
return count == numberOfItems;
})
.ToList();
You could consider adding an extension-method. Something like
public static IEnumerable<T> TakeWhileEqual<T, T2>(this IEnumerable<T> collection, Func<T, T2> predicate)
where T2 : IEquatable<T2>
{
using var iter = collection.GetEnumerator();
if (iter.MoveNext())
{
var first = predicate(iter.Current);
yield return iter.Current;
while (iter.MoveNext() && predicate(iter.Current).Equals(first))
{
yield return iter.Current;
}
}
}
This has the advantage of being efficient, not needing to iterate over the collection more than once. But it does require some more code, even if this can be hidden in an extension method.
I think you probably want to use TakeWhile rather than Take;
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
var n = nums
.GroupBy(i => i)
.OrderByDescending(g => g.Count());
var c = n.First().Count();
var r = n.TakeWhile(g => g.Count() == c)
.Select(g => g.Key);
If you want to do this in a single pass, without LINQ, you can use a Dictionary and a List track
a) how many times you saw a value and
b) what value you saw the most times
c) what other most-values you saw that many times
We skip through the list, trying to look the current value up in the dictionary. It either works or it doesn't - if it works, TryGetValue tells us how many times the current value has been seen. IF it doesn't, TryGetValue gives use a seen of 0. We increment seen. We take a look at how it compares to the max we've seen so far:
It's greater - we have a new leader in the "most frequent" contest - clear the current leaders list and start over with the new n as the leader. Also note the new max
It's equal - we have a tie for the lead; add the current n in among its peers
It's less - we don't care
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
int maxSeen = int.MinValue;
var seens = new Dictionary<int, int>();
var maxes = new List<int>();
foreach(var n in nums){
seens.TryGetValue(n, out var seen);
seens[n] = ++seen;
if(seen > maxSeen){
maxes = new(){n};
maxSeen = seen;
} else if(seen == maxSeen)
maxes.Add(n);
}
You'll end up with maxes as a List<int> that is the list of numbers that appear most.
If you care about allocations of the List's internal array, you could consider clearing the list instead of newing; I new'd because it was a handy one liner to use an initializer with the new leader
You may first group the first input like that.
int[] input = { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
var tmpResult = from i in input
group i by i into k
select new
{
k.Key,
count = k.Count()
};
then you can filter the max value of group like that;
var max = tmpResult.Max(s => s.count);
after you should make a filter is enough
int[] result = tmpResult.Where(f => f.count == max).Select(s => s.Key).ToArray();
Also you can create an Extension method for this.
public static class Extension
{
public static int[] GetMostFrequent(this int[] input)
{
var tmpResult = from i in input
group i by i into k
select new
{
k.Key,
count = k.Count()
};
var max = tmpResult.Max(s => s.count);
return tmpResult.Where(f => f.count == max).Select(s => s.Key).ToArray();
}
You were very close. Just add one more line to your code.
int[] input = { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
var counts = input
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count()})
.OrderByDescending(i => i.Count);
var maxCount = counts.First().Count;
var result = counts
.Where(i=> i.Count == maxCount)
.Select(i => i.Number);
result
{1,6}
How can I find the set of items that occur in 2 or more sequences in a sequence of sequences?
In other words, I want the distinct values that occur in at least 2 of the passed in sequences.
Note:
This is not the intersect of all sequences but rather, the union of the intersect of all pairs of sequences.
Note 2:
The does not include the pair, or 2 combination, of a sequence with itself. That would be silly.
I have made an attempt myself,
public static IEnumerable<T> UnionOfIntersects<T>(
this IEnumerable<IEnumerable<T>> source)
{
var pairs =
from s1 in source
from s2 in source
select new { s1 , s2 };
var intersects = pairs
.Where(p => p.s1 != p.s2)
.Select(p => p.s1.Intersect(p.s2));
return intersects.SelectMany(i => i).Distinct();
}
but I'm concerned that this might be sub-optimal, I think it includes intersects of pair A, B and pair B, A which seems inefficient. I also think there might be a more efficient way to compound the sets as they are iterated.
I include some example input and output below:
{ { 1, 1, 2, 3, 4, 5, 7 }, { 5, 6, 7 }, { 2, 6, 7, 9 } , { 4 } }
returns
{ 2, 4, 5, 6, 7 }
and
{ { 1, 2, 3} } or { {} } or { }
returns
{ }
I'm looking for the best combination of readability and potential performance.
EDIT
I've performed some initial testing of the current answers, my code is here. Output below.
Original valid:True
DoomerOneLine valid:True
DoomerSqlLike valid:True
Svinja valid:True
Adricadar valid:True
Schmelter valid:True
Original 100000 iterations in 82ms
DoomerOneLine 100000 iterations in 58ms
DoomerSqlLike 100000 iterations in 82ms
Svinja 100000 iterations in 1039ms
Adricadar 100000 iterations in 879ms
Schmelter 100000 iterations in 9ms
At the moment, it looks as if Tim Schmelter's answer performs better by at least an order of magnitude.
// init sequences
var sequences = new int[][]
{
new int[] { 1, 2, 3, 4, 5, 7 },
new int[] { 5, 6, 7 },
new int[] { 2, 6, 7, 9 },
new int[] { 4 }
};
One-line way:
var result = sequences
.SelectMany(e => e.Distinct())
.GroupBy(e => e)
.Where(e => e.Count() > 1)
.Select(e => e.Key);
// result is { 2 4 5 7 6 }
Sql-like way (with ordering):
var result = (
from e in sequences.SelectMany(e => e.Distinct())
group e by e into g
where g.Count() > 1
orderby g.Key
select g.Key);
// result is { 2 4 5 6 7 }
May be fastest code (but not readable), complexity O(N):
var dic = new Dictionary<int, int>();
var subHash = new HashSet<int>();
int length = array.Length;
for (int i = 0; i < length; i++)
{
subHash.Clear();
int subLength = array[i].Length;
for (int j = 0; j < subLength; j++)
{
int n = array[i][j];
if (!subHash.Contains(n))
{
int counter;
if (dic.TryGetValue(n, out counter))
{
// duplicate
dic[n] = counter + 1;
}
else
{
// first occurance
dic[n] = 1;
}
}
else
{
// exclude duplucate in sub array
subHash.Add(n);
}
}
}
This should be very close to optimal - how "readable" it is depends on your taste. In my opinion it is also the most readable solution.
var seenElements = new HashSet<T>();
var repeatedElements = new HashSet<T>();
foreach (var list in source)
{
foreach (var element in list.Distinct())
{
if (seenElements.Contains(element))
{
repeatedElements.Add(element);
}
else
{
seenElements.Add(element);
}
}
}
return repeatedElements;
You can skip already Intesected sequences, this way will be a little faster.
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source)
{
var result = new List<T>();
var sequences = source.ToList();
for (int sequenceIdx = 0; sequenceIdx < sequences.Count(); sequenceIdx++)
{
var sequence = sequences[sequenceIdx];
for (int targetSequenceIdx = sequenceIdx + 1; targetSequenceIdx < sequences.Count; targetSequenceIdx++)
{
var targetSequence = sequences[targetSequenceIdx];
var intersections = sequence.Intersect(targetSequence);
result.AddRange(intersections);
}
}
return result.Distinct();
}
How it works?
Input: {/*0*/ { 1, 2, 3, 4, 5, 7 } ,/*1*/ { 5, 6, 7 },/*2*/ { 2, 6, 7, 9 } , /*3*/{ 4 } }
Step 0: Intersect 0 with 1..3
Step 1: Intersect 1 with 2..3 (0 with 1 already has been intersected)
Step 2: Intersect 2 with 3 (0 with 2 and 1 with 2 already has been intersected)
Return: Distinct elements.
Result: { 2, 4, 5, 6, 7 }
You can test it with the below code
var lists = new List<List<int>>
{
new List<int> {1, 2, 3, 4, 5, 7},
new List<int> {5, 6, 7},
new List<int> {2, 6, 7, 9},
new List<int> {4 }
};
var result = lists.UnionOfIntersects();
You can try this approach, it might be more efficient and also allows to specify the minimum intersection-count and the comparer used:
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source
, int minIntersectionCount
, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
foreach (T item in source.SelectMany(s => s).Distinct(comparer))
{
int containedInHowManySequences = 0;
foreach (IEnumerable<T> seq in source)
{
bool contained = seq.Contains(item, comparer);
if (contained) containedInHowManySequences++;
if (containedInHowManySequences == minIntersectionCount)
{
yield return item;
break;
}
}
}
}
Some explaining words:
It enumerates all unique items in all sequences. Since Distinct is using a set this should be pretty efficient. That can help to speed up in case of many duplicates in all sequences.
The inner loop just looks into every sequence if the unique item is contained. Thefore it uses Enumerable.Contains which stops execution as soon as one item was found(so duplicates are no issue).
If the intersection-count reaches the minum intersection count this item is yielded and the next (unique) item is checked.
That should nail it:
int[][] test = { new int[] { 1, 2, 3, 4, 5, 7 }, new int[] { 5, 6, 7 }, new int[] { 2, 6, 7, 9 }, new int[] { 4 } };
var result = test.SelectMany(a => a.Distinct()).GroupBy(x => x).Where(g => g.Count() > 1).Select(y => y.Key).ToList();
First you make sure, there are no duplicates in each sequence. Then you join all sequences to a single sequence and look for duplicates as e.g. here.
I have an ordered list, largest to smallest.
{ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }
I want a list like this
{ 10, 8, 6, 4, 2, 1, 3, 5, 7, 9 }
If you can see, the new order is, first by each odd index ascending, then by each even index descending.
The idea is that each half of the list has roughly the same weight. e.g
{ 10, 8, 6, 4, 2 } = 30
{ 1, 3, 5, 7, 9 } = 25
The title is the best I can explain in a sentence what I'm looking for which is why I had trouble finding the answer from Google.
Here is my go in C#. I'll welcome any comment on my attempt but I'm only looking of the algorithms name, if it has one.
var firstHalf = new List<string>();
var secondHalf = new List<string>();
for (int i = 0; i < originalList.Count; i++)
{
if (i % 2 == 1)
{
firstHalf.Add(originalList[i]);
}
else
{
secondHalf.Add(originalList[i]);
}
}
secondHalf.Reverse();
var finalList = new List<string>(firstHalf);
finalList.AddRange(secondHalf);
This might not be the most efficient way, but it's easy:
var yourlist = originalList.Where(i => i % 2 == 0)
.OrderBy(i => i)
.Concat(originalList.Where(i => i % 2 != 0)
.OrderByDescending(i => i))
.ToList();
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
say i have a list of numbers, i want to know where in the list do the numbers begin decreasing, in no particular order, an example would be ideal!
1,
2,
2,
3,
3,
4,
4,
5,
5,
4, <= this should be my output
4,
3,
3,
2,
2,
1,
thanks
You could create your own extension method on IEnumerable<TSource>
public static class MyEnumerable
{
public static IEnumerable<TSource> Descending<TSource>(this IEnumerable<TSource> source)
where TSource : IComparable<TSource>
{
using (var e = source.GetEnumerator())
{
TSource previous;
if (e.MoveNext())
{
previous = e.Current;
while (e.MoveNext())
{
if (previous.CompareTo(e.Current) > 0)
yield return e.Current;
previous = e.Current;
}
}
}
}
}
Usage
var input = new List<int>() { 1, 2, 3, 2, 4, 5, 6, 7, 8, 9 };
var firstDescending = input.Descending().First();
Not Linq but the C# way:
public int FirstDecreasingIndex(IList<int> myList)
{
for (int i = 0; i < myList.Count - 1; i++)
{
if (myList[i] > myList[i + 1])
return i+1;
}
return -1; //Or some other value
}
For this input:
{ 1,2,3,2,4,5,6,7,8,9 } The function will return 3 as it's the first index where the lists begins decreasing for the first time.
For a truly functional approach:
List<int> numbers = ...;
var firstDecreasingValue =
numbers.Skip(1).Zip(numbers, (number, previous) => new { number, previous })
.Where(x => x.number.CompareTo(x.previous) < 0)
.Select(x => x.number)
.First();
not totally linq, but close enough I think
var list = new List<int> { 1, 2, 3, 4, 5, 4, 3, 2, 1 };
var index = Enumerable.Range(0, list.Count).First(i => i > 0 && list[i] < list[i - 1]);
And here is a "oneliner" using Enumerable.Aggregate and Tuple:s:
var numbers = new[] { 1, 2, 2, 3, 3, 4, 4, 5, 5, 4, 4, 3, 3, 2, 2, 1 };
var idx = numbers.Aggregate(Tuple.Create(true, 0, 0), (tuple, x) =>
tuple.Item1
? tuple.Item3 <= x
? Tuple.Create(true, tuple.Item2 + 1, x)
: Tuple.Create(false, tuple.Item2, x)
: tuple)
.Item2;
The aggregate tuple stores three values:
Whether or not to continue searching for increases,
array index, incremented until the first decrease is detected,
item value to be used for comparison with subsequent value in array, updated until first decrease is detected.
When aggregation is complete, pick the Item2 value from the tuple, where the index of the first decrease is stored.
Output from the above code would be 9 i.e. the base zero index of the first decreased value. If you instead return Item3 from the aggregate tuple, you get the first decreased value (or the last value if the sequence is entirely non-decreasing).
there are two lists:
List<int> list2 = new List<int>(new[] { 1, 2, 3, 5, 6 }); // missing: 0 and 4
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
how do you compare two lists, find missing numbers in List1 and remove these numbers from List1? To be more precise, I need to find a way to specify starting and ending position for comparison.
I imagine that the proccess should be very similar to this:
Step 1.
int start_num = 3; // we know that comparisons starts at number 3
int start = list2.IndexOf(start_num); // we get index of Number (3)
int end = start + 2; // get ending position
int end_num = list2[end]; // get ending number (6)
now we've got positions of numbers (and numbers themselves) for comparison in List2 (3,5,6)
Step 2. To get positions of numbers in List1 for comparison - we can do the following:
int startlist1 = list1.IndexOf(start_num); // starting position
int endlist1 = list1.IndexOf(end_num); // ending position
the range is following: (3,4,5,6)
Step 3. Comparison. Tricky part starts here and I need a help with it
Basically now we need to compare list2 at (3,5,6) with list1 at (3,4,5,6). The missing number is "4".
// I have troubles with this step but the result will be:
int remove_it = 4; // or int []
Step 4. Odd number removal.
int remove_it = 4;
list1 = list1.Where(a => a != remove_it).ToList();
works great, but what will happen if we have 2 missing numbers? i.e.
int remove_it = 4 // becomes int[] remove_it = {4, 0}
Result As you have guessed the result is new List1, without number 4 in it.
richTextBox1.Text = "" + string.Join(",", list1.ToArray()); // output: 0,1,2,3,5,6
textBox1.Text = "" + start + " " + start_num; // output: 2 3
textBox3.Text = "" + end + " " + end_num; // output: 4 6
textBox2.Text = "" + startlist1; // output: 3
textBox4.Text = "" + endlist1; // output: 6
Can you guy help me out with Step 3 or point me out to the right direction?
Also, can you say what will happen if starting number(start_num) is the last number, but I need to get next two numbers? In example from above numbers were 3,5,6, but they should be no different than 5,6,0 or 6,0,1 or 0,1,2.
Just answering the first part:
var list3 = list1.Intersect(list2);
This will set list3 to { 0, 1, 2, 3, 4, 5, 6 } - { 0, 4 } = { 1, 2, 3, 5, 6 }
And a reaction to step 1:
int start_num = 3; // we know that comparisons starts at number 3
int start = list2.IndexOf(start_num); // we get index of Number (3)
int end = start + 2; // get ending position
From where do you get all those magic numbers (3, + 2 ) ?
I think you are over-thinking this, a lot.
var result = list1.Intersect(list2)
You can add a .ToList on the end if you really need the result to be a list.
List<int> list2 = new List<int>(new[] { 1, 2, 3, 5, 6 }); // missing: 0 and 4
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
// find items in list 2 notin 1
var exceptions = list1.Except(list2);
// or are you really wanting to do a union? (unique numbers in both arrays)
var uniquenumberlist = list1.Union(list2);
// or are you wanting to find common numbers in both arrays
var commonnumberslist = list1.Intersect(list2);
maybe you should work with OrderedList instead of List...
Something like this:
list1.RemoveAll(l=> !list2.Contains(l));
To get the numbers that exist in list1 but not in list2, you use the Except extension method:
IEnumerable<int> missing = list1.Except(list2);
To loop through this result to remove them from list1, you have to realise the result, otherwise it will read from the list while you are changing it, and you get an exception:
List<int> missing = list1.Except(list2).ToList();
Now you can just remove them:
foreach (int number in missing) {
list1.Remove(number);
}
I'm not sure I understand your issue, and I hope the solution I give you to be good for you.
You have 2 lists:
List list2 = new List(new[] { 1, 2, 3, 5, 6 }); // missing: 0 and 4
List list1 = new List(new[] { 0, 1, 2, 3, 4, 5, 6 });
To remove from list1 all the missing numbers in list2 I suggest this solution:
Build a new list with missing numbers:
List diff = new List();
then put all the numbers you need to remove in this list. Now the remove process should be simple, just take all the elements you added in diff and remove from list2.
Did I understand correctly that algorithm is:
1) take first number in List 2 and find such number in List1,
2) then remove everything from list 1 until you find second number form list2 (5)
3) repeat step 2) for next number in list2.?
You can use Intersect in conjunction with Skip and Take to get the intersection logic combined with a range (here we ignore the fact 0 is missing as we skip it):
static void Main(string[] args)
{
var list1 = new List<int> { 1, 2, 3, 4, 5 };
var list2 = new List<int> { 0, 1, 2, 3, 5, 6 };
foreach (var i in list2.Skip(3).Take(3).Intersect(list1))
Console.WriteLine(i); // Outputs 3 then 5.
Console.Read();
}
Though if I'm being really honest, I'm not sure what is being asked - the only thing I'm certain on is the intersect part:
var list1 = new List<int> { 1, 2, 3, 4, 5 };
var list2 = new List<int> { 0, 1, 2, 3, 5, 6 };
foreach (var i in list2.Intersect(list1))
Console.WriteLine(i); // Outputs 1, 2, 3, 5.
ok, seems like I hadn't explained the problem well enough, sorry about it. Anyone interested can understand what I meant by looking at this code:
List<int> list2 = new List<int>() { 1, 2, 3, 5, 6 }; // missing: 0 and 4
List<int> list1 = new List<int>() { 0, 1, 2, 3, 4, 5, 6 };
int number = 3; // starting position
int indexer = list2.BinarySearch(number);
if (indexer < 0)
{
list2.Insert(~index, number); // don't look at this part
}
// get indexes of "starting position"
int index1 = list1.Select((item, i) => new { Item = item, Index = i }).First(x => x.Item == number).Index;
int index2 = list2.Select((item, i) => new { Item = item, Index = i }).First(x => x.Item == number).Index;
// reorder lists starting at "starting position"
List<int> reorderedList1 = list1.Skip(index1).Concat(list1.Take(index1)).ToList(); //main big
List<int> reorderedList2 = list2.Skip(index2).Concat(list2.Take(index2)).ToList(); // main small
int end = 2; // get ending position: 2 numbers to the right
int end_num = reorderedList2[end]; // get ending number
int endlist1 = reorderedList1.IndexOf(end_num); // ending position
//get lists for comparison
reorderedList2 = reorderedList2.Take(end + 1).ToList();
reorderedList1 = reorderedList1.Take(endlist1 + 1).ToList();
//compare lists
var list3 = reorderedList1.Except(reorderedList2).ToList();
if (list3.Count != 0)
{
foreach (int item in list3)
{
list1 = list1.Where(x => x != item).ToList(); // remove from list
}
}
// list1 is the result that I wanted to see
if there are any ways to optimize this code please inform me. cheers.