How to get first index of Binary Search's results? - c#

I have some problems. I have 2 list such as:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
⇒ True result is: {1, 3, 0}
I would like to get the first index of numbers in secondList that exists in firstList. I used list.BinarySearch() but the result was {2, 3, 0}.

List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
var output = secondList.Select(item => firstList.IndexOf(item)); // [1 , 3 , 0]
You can replace the IndexOf with a BinarySearch logic, but BinarySearch returns the first matched element index, so you won't get the lowest number, IndexOf does return the lowest matching index.

The problem is that when the list contains duplicate values as in your case, the BinarySearch method will return the index of any of the matching values (non deterministic).
To get the desired result, you could create and use a custom extension method like this:
public static class ListExtensions
{
public static int BinarySearchFirst<T>(this List<T> source, T item, IComparer<T> comparer = null)
{
if (comparer == null) comparer = Comparer<T>.Default;
int index = source.BinarySearch(item, comparer);
while (index > 0 && comparer.Compare(source[index], source[index - 1]) == 0)
index--;
return index;
}
}
Sample usage:
var result = secondList.Select(x => firstList.BinarySearchFirst(x)).ToList();
// { 1, 3, 0 }

C++ has a standard library function for this called lower_bound().
Here's a C# implementation. This is useful if you are searching large collections:
public static int LowerBound<T>(IList<T> values, T target, int first, int last)
where T : IComparable<T>
{
int left = first;
int right = last;
while (left < right)
{
int mid = left + (right - left) / 2;
var middle = values[mid];
if (middle.CompareTo(target) < 0)
left = mid + 1;
else
right = mid;
}
return left;
}
That doesn't return -1 for elements that it doesn't find, so to fix that we can wrap it like so:
public static int LowerBoundOrMinusOne<T>(IList<T> values, T target, int first, int last)
where T : IComparable<T>
{
int result = LowerBound(values, target, first, last);
if (result >= last || result < first || values[result].CompareTo(target) != 0)
return -1;
return result;
}
Here is how you use it:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1 };
List<int> result = secondList
.Select(value => LowerBoundOrMinusOne(firstList, value, 0, firstList.Count))
.ToList();
Console.WriteLine(string.Join(", ", result));
Of course, this is mainly of benefit to large lists because it has an O(Log2(N)) rather than an O(N) complexity.

Iterate through second array and get index of element in first array:
foreach (int item in secondList)
{
Console.WriteLine(firstList.IndexOf(item));
}

If you have a large firstList and so you have to use BinarySearch try amending it: find out the item (which is not guaranteed to be the leftmost one) by BinarySearch, then move to the left while having read the same item:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1, 123 };
var result = secondList
.Select(item => firstList.BinarySearch(item))
.Select(index => index < 0 ? -1 : Enumerable
.Range(0, index + 1) // we have to scan [0..index] at the worst case
.Select(i => index - i) // scan in reverse
.TakeWhile(i => firstList[index] == firstList[i]) // take while items are the same
.Last()); // finally, we want the last item
Test
// 1, 3, 0, -1
Console.Write(String.Join(", ", result));

Related

merging 2 sorted integer list into one ,in ascending order. my method is getting too messy

static List<int>Merge(List<int> list_a, List<int> list_b)
{
List<int> list_c=new List<int>();
int countA = 0, countB = 0;
for (int i =0;i< list_a.Count + list_b.Count-2;i++)
{
if (list_a[countA]<=list_b[countB])
{
list_c.Add(list_a[countA]);
countA ++;
}
else
{
list_c.Add(list_b[countB]);
countB ++;
}
}
return list_c;
}
my idea was to go through the for loop as many times as how many element list_c will have at the end
Compare each element in both list then add the smallest one in list_c
i already have a way to check if both lists are in ascending order
when im testing it with
List<int> myList1 = new List<int> { 1, 2, 3, 7, 8, 9 };
List<int> myList2 = new List<int> { 4, 5, 6};
Console.WriteLine("new list :{ " + string.Join(",", Merge(myList1, myList2)));
countB goes out of bound once the last element in list b is added, the next comparison in that for-loop is then invalid as its comparing list_b[3]
Your index on the shorter array is exceed its maximum index. Need to check whether a Count is exceed the maximum index.
class Program {
static List<int> Merge(List<int> list_a, List<int> list_b) {
List<int> list_c = new List<int>();
int countA = 0, countB = 0;
for (int i = 0; i < list_a.Count + list_b.Count; i++) {
if (countA < list_a.Count && countB < list_b.Count) {
if (list_a[countA] <= list_b[countB]) {
list_c.Add(list_a[countA]);
countA++;
}
else {
list_c.Add(list_b[countB]);
countB++;
}
}
else if (countA < list_a.Count) {
list_c.Add(list_a[countA]);
countA++;
}
else {
list_c.Add(list_b[countB]);
countB++;
}
}
return list_c;
}
static void Main(string[] args) {
List<int> myList1 = new List<int> { 1, 2, 3, 7, 8, 9 };
List<int> myList2 = new List<int> { 4, 5, 6 };
Console.WriteLine("new list :{ " + string.Join(",", Merge(myList1, myList2)) + "}");
}
}
Considering you want to use cycles:
public static List<int> Merge(List<int> list_a, List<int> list_b)
{
int firstListIndexer = 0, secondListIndexer = 0;
List<int> list_c = new List<int>();
// Traverse lists, until one of them run out of the elements
while (firstListIndexer < list_a.Count && secondListIndexer < list_b.Count)
{
if (list_a[firstListIndexer] < list_b[secondListIndexer])
list_c.Add(list_a[firstListIndexer++]);
else
list_c.Add(list_b[secondListIndexer++]);
}
// Store remaining elements of first list
while (firstListIndexer < list_a.Count)
list_c.Add(list_a[firstListIndexer++]);
// Store remaining elements of second list
while (secondListIndexer < list_b.Count)
list_c.Add(list_b[secondListIndexer++]);
return list_c;
}
Also, you can read this to improve your knowledge on the subject.
If we can assume that both lists are ascendingly ordered then you can merge the collections like this to respect ascending ordering.
static List<int> MergeTowAscendinglyOrderedCollections(IEnumerable<int> collectionA, IEnumerable<int> collectionB)
{
var result = new List<int>();
IEnumerator<int> iteratorA = collectionA.GetEnumerator();
IEnumerator<int> iteratorB = collectionB.GetEnumerator();
bool doesIteratorAHaveRemainingItem = iteratorA.MoveNext();
bool doesIteratorBHaveRemainingItem = iteratorB.MoveNext();
void SaveIteratorAsCurrentAndAdvanceIt()
{
result.Add(iteratorA.Current);
doesIteratorAHaveRemainingItem = iteratorA.MoveNext();
}
void SaveIteratorBsCurrentAndAdvanceIt()
{
result.Add(iteratorB.Current);
doesIteratorBHaveRemainingItem = iteratorB.MoveNext();
}
do
{
if (iteratorA.Current < iteratorB.Current)
{
if (doesIteratorAHaveRemainingItem) SaveIteratorAsCurrentAndAdvanceIt();
else SaveIteratorBsCurrentAndAdvanceIt();
}
else if (iteratorA.Current > iteratorB.Current)
{
if (doesIteratorBHaveRemainingItem) SaveIteratorBsCurrentAndAdvanceIt();
else SaveIteratorAsCurrentAndAdvanceIt();
}
else if (iteratorA.Current == iteratorB.Current)
{
SaveIteratorAsCurrentAndAdvanceIt();
SaveIteratorBsCurrentAndAdvanceIt();
}
} while (doesIteratorAHaveRemainingItem || doesIteratorBHaveRemainingItem);
return result;
}
In case of duplication I've added both numbers to the merged list but depending your business requirements you can adjust the code to omit one or both values from the result.
you can do a Union
List<int> myList1 = new List<int> { 1, 2, 3, 7, 8, 9 };
List<int> myList2 = new List<int> { 4, 5, 6};
var merged = myList1.Union(myList2).OrderBy(o=>o).ToList();
foreach(int number in merged)
Console.WriteLine(number);
output as follows
1
2
3
4
5
6
7
8
9
If you have to implement your serlf:
Implementation with no counters and no indexes in just a few lines using Coroutines:
class Program {
static void Main() {
List<int> l1 = new List<int>() { 9,8, 7, 5, 3, 1 };
List<int> l2 = new List<int>() {12 ,10, 8, 6, 4, 2 };
IEnumerable<int> res = MergeAscending(sl1, sl2);
foreach (int item in res) {
Console.Write($"{item},");
}
Console.Read();
}
static IEnumerable<T> MergeAscending<T>(IEnumerable<T> l1, IEnumerable<T> l2) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable {
IEnumerator<T> e1 = l1.AsParallel().OrderBy(e => e).GetEnumerator();
IEnumerator<T> e2 = l2.AsParallel().OrderBy(e => e).GetEnumerator();
IEnumerator<T> longest; //to yield longest list remains
//First move needed to init first "Current"
e1.MoveNext();
e2.MoveNext();
//yields smaller current value and move its IEnumerable pointer
//breaks while loop if no more values in some Enumerable and mark the other one as longest
while (true) {
if (e1.Current.CompareTo(e2.Current) < 0) {
yield return e1.Current;
if (!e1.MoveNext()) { longest = e2; break; }
}
else {
yield return e2.Current;
if (!e2.MoveNext()) { longest = e1; break; }
}
}
//finish the longest Enumerator
do {
yield return longest.Current;
} while (longest.MoveNext());
}
}
Anyway, my recomendation is just as Siavash in the comments:
var merged = myList1.Union(myList2).AsParallel().OrderBy(e => e).ToList();

How Do You Sort Items By Set in LINQ?

I have this simple array with the following items:
[4, 3, 1, 1, 0, 0]
I want to sort it on this way:
[4, 3, 1, 0, 1, 0]
As you notice, I want to sort it on descending order. But on this case, there are two sets of array that was sorted on descending order:
4, 3, 1, 0 and 1, 0
Which produce the output:
[4, 3, 1, 0, 1, 0]
I tried to do this using Group By:
var result = arrayInput.GroupBy(c => c).Select(a => a.OrderBy(d => d).First());
But this produces only 4, 3, 1, 0 and I need to append the not selected integers as sorted so that it will be:
[4, 3, 1, 0, 1, 0]
Here's how I'd do that. Basically you want to try putting each number into a group of sets. The first one that doesn't already have that number is the one it actually goes in and if none have it then you add a new set. In this way the first set will have all unique numbers, the second with have all numbers that are duplicated as least once, and so on. Then at the end you return items for each of the sets ordering each set as you go.
public static IEnumerable<int> SetSort(this IEnumerable<int> nums) {
var sets = new List<HashSet<int>>();
foreach(var num in nums) {
bool added = false;
foreach(var set in sets) {
added = set.Add(num);
if(added) break;
}
if(!added){
sets.Add(new HashSet<int> { num });
}
}
foreach(var set in sets) {
foreach(var num in set.OrderByDescending(x => x)) {
yield return num;
}
}
}
As usually Aggregate extension method can do everything
var data = new[] { 1, 2, 3, 4, 1, 0, 1, 0 };
var result =
data.GroupBy(i => i)
.OrderByDescending(group => group.Key)
.Aggregate(new { Keys = new List<int>(), Duplicates = new List<int>() },
(lists, group) =>
{
lists.Keys.Add(group.Key);
var duplicates = Enumerable.Repeat(group.Key, group.Count() - 1);
lists.Duplicates.AddRange(duplicates);
return lists;
},
lists => lists.Keys.Concat(lists.Duplicates));
// result is new[] { 4, 3, 2, 1, 0, 1, 1, 0 };
Approach with immutable collections
private IEnumerable<int> SortFunc(IEnumerable<int> data)
{
var ordered =
data.GroupBy(i => i)
.OrderByDescending(group => group.Key)
.Select(group => new
{
Key = group.Key,
Duplicates = group.Skip(1)
});
foreach (var key in ordered.Select(group => group.Key))
{
yield return key;
}
foreach (var value in ordered.SelectMany(group => group.Duplicates))
{
yield return value;
}
}

Find a series of the same number in a List

I have a List of items containing either 1 or 0, I'm looking to output the items only where there are six 1's back to back in the list. So only write to the console if the item in this list is part of a group of six.
1
1
1
1
1
1
0
1
1
1
0
In the above list, the first six items would be output but the bottom set of three 1s would not as they are not part of a group of six.
Is this a job for LINQ or RegEx?
You can concatenate all values into string, then split it by zeros. From substrings select those which have at least 6 characters:
List<int> values = new List<int> { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
var series = String.Concat(values)
.Split(new[] { '0' }, StringSplitOptions.RemoveEmptyEntries)
.Where(s => s.Length >= 6);
For given input data series will contain single item "111111" which you can output to console.
Classic run length encoding, O(n), lazy evaluated, stack agnostic, generic for any equatable type.
public void TestRunLength()
{
var runs = new List<int>{ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 0, 4};
var finalGroup = RunLength(runs).FirstOrDefault(i => i.Count == 6 && i.First() == 1);
}
private IEnumerable<List<T>> RunLength<T>(IEnumerable<T> source) where T : IEquatable<T>
{
T current = default(T);
var requiresInit = true;
var list = new List<T>();
foreach (var i in source)
{
if (requiresInit)
{
current = i;
requiresInit = false;
}
if (i.Equals(current))
{
list.Add(i);
}
else
{
yield return list;
list = new List<T>{ i };
current = i;
}
}
if (list.Any())
{
yield return list;
}
}
And because it's lazy it works on infinite sequences (yes I know its not infinite, but it is large)!
public void TestRunLength()
{
var random = new Random();
var runs = Enumerable.Range(int.MinValue, int.MaxValue)
.Select(i => random.Next(0, 10));
var finalGroup = RunLength(runs)
.FirstOrDefault(i => i.Count == 6);
}
Probably it can be done with Regex too if you concatenate your numbers into a string. But I would prefer linq:
var bits = new List<int> {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0};
int bitCountPerGroup = 6;
var result = bits // (1) (2)
.Select((x,idx) => bits.Skip(idx).TakeWhile(y => y == x))
.Where(g => g.Count() == bitCountPerGroup); // (3)
foreach (var set in result)
Console.WriteLine(string.Join(" ", set));
This code gets a number-set for each number by starting from the number (1) and taking the next numbers as long as they are equal (2). Then filter the groups and gets only those groups which have 6 numbers (3).
If for example your list is of an unknown size,or better,you do not know the items in it you could do this recursive example(note that i placed more zeros so it would fetch 2 sets of data,it works with yours also),and pass to the method the amout to group by:
//this is the datastructure to hold the results
static List<KeyValuePair<string, List<int>>> Set = new List<KeyValuePair<string, List<int>>>();
private static void GetData(List<int> lst, int group)
{
int count = 1;
int pivot = lst.First();
if (lst.Count < group)
{
return;
}
else
{
foreach (int i in lst.Skip(1))
{
if (i == pivot)
{
count++;
}
else if (count == group)
{
Set.Add(new KeyValuePair<string, List<int>>("Set of items " + pivot, lst.Take(count).ToList()));
GetData(lst.Skip(count).ToList(), group);
break;
}
else
{
GetData(lst.Skip(count).ToList(), group);
break;
}
}
}
}
Then in Main():
static void Main(string[] args)
{
List<int> test = new List<int> { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
GetData(test, 6);
foreach (var item in Set)
{
Console.WriteLine("\t" + item.Key);
foreach (var subitem in item.Value)
{
Console.WriteLine(subitem);
}
}
}

Linq fill function

Is there a Linq operator that will ensure that a collection is a minimum size
What I would like to see is:
int[] x = {1, 2, 3, 4};
var y = x.Fill(6);
// y is now {1, 2, 3, 4, 0, 0}
Note (from the answers so far) I'm looking for something that will work with IEnumerable<T>. int[] was just for easy initialization in the example
No, but an extension method wouldn't be hard:
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> source, int length)
{
int i = 0;
// use "Take" in case "length" is smaller than the source's length.
foreach(var item in source.Take(length))
{
yield return item;
i++;
}
for( ; i < length; i++)
yield return default(T);
}
Usage:
int[] x = {1, 2, 3, 4};
var y = x.PadRight(6);
// y is now {1, 2, 3, 4, 0, 0}
y = x.PadRight(3);
// y is now {1, 2, 3}
No, but you can combine Concat, Repeat and Take to achieve this.
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> source, int length) =>
source.Concat(Enumerable.Repeat(default(T), length)).Take(length);
There's nothing in Linq that would do that in a single call - also note that because linq is for querying not modifying, any such method wouldn't change your collection anyway (but might return an extended version of your collection).
That said, an extension method like this would return such a result:
public static IEnumerable<T> Fill<T>(this IEnumerable<T> target, int minLength)
{
int i = 0;
foreach (var item in target)
{
i++;
yield return item;
}
while (i < minLength)
{
i++;
yield return default(T);
}
}
int minLength = 6;
var x = new[] { 1, 2, 3, 4 };
x.Select(i => { minLength--; return i; }).Concat(
Enumerable.Repeat(0, minLength).TakeWhile(i => minLength-- > 0));

Removing sequential repeating items from List<T> using linq

I'm looking for a way to prevent repeating items in a list but still preserve the order.
For example
1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4
should become
1, 2, 3, 4, 1, 2, 3, 4
I've done it quite inelegantly using a for loop, checking the next item as follows
public static List<T> RemoveSequencialRepeats<T>(List<T> input)
{
var result = new List<T>();
for (int index = 0; index < input.Count; index++)
{
if (index == input.Count - 1)
{
result.Add(input[index]);
}
else if (!input[index].Equals(input[index + 1]))
{
result.Add(input[index]);
}
}
return result;
}
Is there a more elegant way to do this, preferably with LINQ?
You can create extension method:
public static IEnumerable<T> RemoveSequentialRepeats<T>(
this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
var comparer = EqualityComparer<T>.Default;
if (!iterator.MoveNext())
yield break;
var current = iterator.Current;
yield return current;
while (iterator.MoveNext())
{
if (comparer.Equals(iterator.Current, current))
continue;
current = iterator.Current;
yield return current;
}
}
}
Usage:
var result = items.RemoveSequentialRepeats().ToList();
You can also use pure LINQ:
List<int> list = new List<int>{1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4};
var result = list.Where((x, i) => i == 0 || x != list[i - 1]);
If you really really hate the world, pure LINQ:
var nmbs = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4, 5 };
var res = nmbs
.Take(1)
.Concat(
nmbs.Skip(1)
.Zip(nmbs, (p, q) => new { prev = q, curr = p })
.Where(p => p.prev != p.curr)
.Select(p => p.curr));
But note that you'll need to enumerate (at least partially) the enumerable 3 times (the Take, the "left" part of Zip, the first parameters of Zip). This method is slower than building a yield method or doing it directly.
Explanation:
You take the first number (.Take(1))
You take all the numbers from the second (.Skip(1)) and pair it with all the numbers (.Zip(nmbs). We will call curr the numbers from the first "collection" and prev the numbers from the second "collection" ((p, q) => new { prev = q, curr = p })). You then take only the numbers that are different from the previous number (.Where(p => p.prev != p.curr)) and from these you take the curr value and discard the prev value (.Select(p => p.curr))
You concat these two collections (.Concat()
you could write simple LINQ :
var l = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
var k = new Nullable<int>();
var nl = l.Where(x => { var res = x != k; k = x; return res; }).ToArray();
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
or pythonic (well, my best try) way:
l.Zip(l.Skip(1), (x, y) => new[] { x, y })
.Where(z => z[0] != z[1]).Select(a => a[0])
.Concat(new[] { l[l.Length - 1] }).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
the simplest one (edit: haven't seen that it already suggested by King King)
l.Where((x, i) => i == l.Length - 1 || x != l[i + 1]).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
If you want LINQ statement that do not rely on captured value of result inside the call you'll need some construct with aggregate as it is the only method that carries value along with operation. I.e. based on Zaheer Ahmed's code:
array.Aggregate(new List<string>(),
(items, element) =>
{
if (items.Count == 0 || items.Last() != element)
{
items.Add(element);
}
return items;
});
Or you can even try to build list without if:
array.Aggregate(Enumerable.Empty<string>(),
(items, element) => items.Concat(
Enumerable.Repeat(element,
items.Count() == 0 || items.Last() != element ? 1:0 ))
);
Note to get reasonable performance of above samples with Aggregate you'd need to also carry last value (Last will have to iterate whole sequence on each step), but code that carries 3 values {IsEmpty, LastValue, Sequence} in a Tuple is very strange looking. These samples are here for entertaining purposes only.
One more option is to Zip array with itself shifted by 1 and return elements that are not equal...
More practical option is to build iterator that filters values:
IEnumerable<string> NonRepeated(IEnumerable<string> values)
{
string last = null;
bool lastSet = false;
foreach(var element in values)
{
if (!lastSet || last != element)
{
yield return element;
}
last = element;
lastSet = true;
}
}
check if last of new list and current item is not same then add to new list:
List<string> results = new List<string>();
results.Add(array.First());
foreach (var element in array)
{
if(results[results.Length - 1] != element)
results.Add(element);
}
or using LINQ:
List<int> arr=new List<int>(){1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
List<int> result = new List<int>() { arr.First() };
arr.Select(x =>
{
if (result[result.Length - 1] != x) result.Add(x);
return x;
}).ToList();
Do have proper validation for null object.
Try this:
class Program
{
static void Main(string[] args)
{
var input = "1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 ";
var list = input.Split(',').Select(i => i.Trim());
var result = list
.Select((s, i) =>
(s != list.Skip(i + 1).FirstOrDefault()) ? s : null)
.Where(s => s != null)
.ToList();
}
}
Here the code you need :
public static List<int> RemoveSequencialRepeats(List<int> input)
{
var result = new List<int>();
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
return result;
}
The LINQ magic is:
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
Or you can create extension method like this:
public static class Program
{
static void Main(string[] args)
{
List<int> numList=new List<int>(){1,2,2,2,4,5,3,2};
numList = numList.RemoveSequentialRepeats();
}
public static List<T> RemoveSequentialRepeats<T>(this List<T> p_input)
{
var result = new List<T> { p_input.First() };
result.AddRange(p_input.Where(p_element => !result.Last().Equals(p_element)));
return result;
}
}
If you feel like referencing an F# project you can write
let rec dedupe = function
| x::y::rest when x = y -> x::dedupe rest
| x::rest -> x::dedupe rest
| _ -> []

Categories

Resources