Getting wrong range of numbers - c#

Given the pair of 2 strings "2-4,6-8" I want to separate these 2 pairs and find all numbers between those range.
So first pair 2-4 should return me 2, 3, 4
Second pair 6-8 should return 6, 7, 8
I tried below code
var splittedString = ln.Split(",");
var firstPair = splittedString[0];
var secondPair = splittedString[1];
var splittedFirstPair = firstPair.Split("-");
IEnumerable<int> firsPairRange = Enumerable.Range(
Convert.ToInt32(splittedFirstPair[0]),
Convert.ToInt32(splittedFirstPair[1]));
var splittedSecondPair = secondPair.Split("-");
IEnumerable<int> secondPairRange = Enumerable.Range(
Convert.ToInt32(splittedSecondPair[0]),
Convert.ToInt32(splittedSecondPair[1]));
But the variable firsPairRange gives me output 2,3,4,5 and the variable secondPairRange gives me output 6,7,8,9,10,11,12,13
I don't understand why and how to fix it?

Instead of Enumerable.Range(start,end), the second parameter needs to be the count of elements including the last element
Enumerable.Range(start,end-start+1)
string ln = "2-4,6-8";
var splittedString = ln.Split(',').Select(x => x.Split('-').Select(int.Parse).ToArray());
int[] first = splittedString.ElementAt(0);
int[] second = splittedString.ElementAt(1);
var firstPairRange = Enumerable.Range(first[0], first[1] - first[0] + 1);
var secondPairRange = Enumerable.Range(second[0], second[1] - second[0] + 1);

Enumerable.Range has two parameters:
the start
the count(!)
So this gives you already enough information to fix it. Your start is 2 and the count is 4 because of 2-4 but you want 4-2+1=3 as count:
IEnumerable<int> firstPairRange = Enumerable.Empty<int>();
if (splittedFirstPair.Length == 2
&& int.TryParse(splittedFirstPair[0], out int first)
&& int.TryParse(splittedFirstPair[1], out int second)
&& second >= first)
{
int count = second - first + 1;
firstPairRange = Enumerable.Range(first, count);
}

In general case (negative numbers, single numbers) when we allow strings like this
-9,2-4,6-8,-5,7,-3-5,-8--2
we can put it as
private static IEnumerable<IEnumerable<int>> Ranges(string text) {
foreach (var range in text.Split(',')) {
var match = Regex.Match(range,
#"^\s*(?<left>-?[0-9]+)\s*-\s*(?<right>-?[0-9]+)\s*$");
if (match.Success) {
int left = int.Parse(match.Groups["left"].Value);
int right = int.Parse(match.Groups["right"].Value);
yield return Enumerable.Range(left, right - left + 1);
}
else
yield return new[] { int.Parse(range) };
}
}
Demo:
string text = "-9,2-4,6-8,-5,7,-3-5,-8--2";
var result = string.Join(Environment.NewLine, Ranges(text)
.Select(range => string.Join(", ", range)));
Console.Write(result);
Output:
-9
2, 3, 4
6, 7, 8
-5
7
-3, -2, -1, 0, 1, 2, 3, 4, 5
-8, -7, -6, -5, -4, -3, -2

You are providing wrong values to Enumerable.Range() function. Its syntax is Enumerable.Range(int start, int count). In start, you have to give first integer in the sequence and in count, you have to give number of integers to be generated in sequence. If you correct your count, right sequence will be generated. Replace your lines with the following lines:
IEnumerable<int> firsPairRange = Enumerable.Range(Convert.ToInt32(splittedFirstPair[0]), Convert.ToInt32(splittedFirstPair[1]) - Convert.ToInt32(splittedFirstPair[0]) + 1);
IEnumerable<int> secondPairRange = Enumerable.Range(Convert.ToInt32(splittedSecondPair[0]), Convert.ToInt32(splittedSecondPair[1]) - Convert.ToInt32(splittedSecondPair[0]) + 1);

Related

Access last 3 elements of the array in c# and perform action in a single statement

For example, I have n number of integer array elements in c#. Can we access the last 3 elements of the array and modify these elements for instance multiple each element by 4. Can we achieve this in a single statement instead of using foreach/for loop(can we use the regular expression)?
Before operation
arr[0] = 3
..
..
arr[n-3] = 1
arr[n-2] = 5
arr[n-1] = 6
After operation
arr[0] = 3
..
..
arr[n-3] = 4
arr[n-2] = 20
arr[n-1] = 24
You can do this:
var arr = new int[] {1, 2, 3, 4, 5};
var result = arr.TakeLast(3).Select(x => x * 4).ToArray();
p.s. this is done in .NET 6
With C# 8 (i.e. dotnet core 3) and later you can also use Ranges to write as a one-liner:
int[] a = { 3, 4, 5, 6, 7, 8, 9, 1, 5, 6 };
var result = a[..^3].Concat(a[^3..].Select(e => e * 4));
a[..^3] does return all elements except the last 3.
a[^3..] does return the last 3 elements, which are then multiplied by 4.
The two arrays are then concatenated.
If you want to use result as an array, add ToArray().
If you do not have a formula that applies to all of them you could use tuples:
int []x = new[] {10, 20, 30, 40, 50, 60};
var len =x.Length;
(x[len-3], x[len-2], x[len-1]) = (6, 7659, 854);
Of course, this only works if the array has at least 3 elements. If you have a formula and need to select a dynamic number of items for update, then #Jim de Vries' answer works best.
You can use arr.Length and sub by 3, 2, 1 Like:
int[] arr = new int[] { 1, 2, 3, 4, 5, 6 };
int arrLength = arr.Length;
Console.WriteLine("Before Operation: {0}", string.Join(", ", arr));
arr[arrLength - 3] *= 4;
arr[arrLength - 2] *= 4;
arr[arrLength - 1] *= 4;
Console.WriteLine("After operation: {0}", string.Join(", ", arr));
You can use LINQ for same. Below is the sample code.
int[] arr = new[] { 1, 3, 3, 3, 5 };
int elementsCount = 3;
int multiplier = 4;
var result = arr.Take(arr.Length - elementsCount).Concat(arr.Skip(arr.Length - elementsCount).Select(a => a * multiplier)).ToArray();

Consecutive points alternating up and down in a list

I have an observation list and want to find consecutive alternating up and down values for a given count. For example when observation list is { 1, 3, 4, 2, 7, 5, 6, 8, 1, 2, 4} and given count for check is 4, method should return a list<list>> as {{3,4,2,7}, {2,7,5,6}}.
I already create method as below, but want to do this with linq or more efficent way.Could anybody help?
List<List<decimal>> GetinvalidObservationMatrix(List<decimal> observationList, int checkedCount)
{
List<List<decimal>> invalidObservationMatrix = new List<List<decimal>>();
while (observationList.Count >= checkedCount)
{
List<decimal> currentObservationList = observationList.Take(checkedCount).ToList();
bool isGreater = false;
bool isPreviousGreater = false;
for (int i = 1; i < checkedCount - 1; i++)
{
isPreviousGreater = isGreater;
if (currentObservationList[i] == currentObservationList[i - 1] || currentObservationList[i] == currentObservationList[i + 1])
{
break;
}
if (currentObservationList[i] > currentObservationList[i - 1])
{
isGreater = true;
}
else
{
isGreater = false;
}
if (i != 1 && isGreater == isPreviousGreater)
{
break;
}
if (isGreater)
{
if (currentObservationList[i + 1] >= currentObservationList[i])
{
break;
}
}
else
{
if (currentObservationList[i + 1] <= currentObservationList[i])
{
break;
}
}
if (i == checkedCount - 2)
{
invalidObservationMatrix.Add(currentObservationList);
}
}
observationList = observationList.Skip(1).ToList();
}
return invalidObservationMatrix;
}
Accepting that this does not actually answer the original question, this code will find all the alternating sequences.
void FindAlternativeSequences()
{
var observations = new List<int>() { 1, 3, 4, 2, 7, 5, 6, 8, 1, 2, 4 };
const int threshold = 4;
var consecutivePairs = observations.Skip(1).Zip(observations, (a, b) => Math.Sign(a - b)).Zip(GetAlternator(), (x, y) => x * y);
var runs = FindEqualRuns(consecutivePairs).Select(t => new Tuple<int, int>(t.Item1, t.Item2 + 1)).ToList();
}
public IEnumerable<int> GetAlternator()
{
int value = -1;
int sanity = Int32.MaxValue;
while (--sanity > 0)
{
value *= -1;
yield return value;
}
yield break;
}
public IEnumerable<Tuple<int,int>> FindEqualRuns(IEnumerable<int> enumerable)
{
int previousValue = 0;
int index = 0;
int startIndex = 0;
bool foundAnElement = false;
foreach ( var value in enumerable )
{
if (index == 0) previousValue = value;
foundAnElement = true;
if (!value.Equals(previousValue))
{
// This is a difference, return the previous run
yield return new Tuple<int, int>(startIndex, index - startIndex);
startIndex = index;
previousValue = value;
}
index++;
}
if (foundAnElement)
{
yield return new Tuple<int, int>(startIndex, index - startIndex);
}
yield break;
}
What it does
The code finds all alternating sequences of any length, starting with either an up-step or a down-step. It then populates the runs variable with tuples where Item1 is the index of the first element in the sequence, and Item2 is the length of the alternating sequence.
This runs variable can then be used to ask a wide range of questions about the data, such as deriving the answer to the original question. The runs output by itself does not return the alternating sequences, but identifies where and how long they are.
How it works
The bulk of the of the work is done in one line:
var consecutivePairs = observations.Skip(1).Zip(observations, (a, b) => Math.Sign(a - b)).Zip(GetAlternator(), (x, y) => x * y);
This zips together the observations along with observations-skip-1 to give pairs of consecutive values. These pairs are then subtracted and run through the Math.Sign() function to give +1, -1 or 0 for whether the first is greater, the second is greater, or if they are the same. This output has runs of +1 or -1 for increasing or decreasing sequences. It's also one element shorter than observations.
observations => { 1, 3, 4, 2, 7, 5, 6, 8, 1, 2, 4 }
Output from 1st .Zip() => { 1, 1, -1, 1, -1, 1, 1, -1, 1, 1 }
This result is then zipped and multiplied by an alternating +1, -1, +1, -1, ... sequence. This results in runs of +1 or -1 indicating consecutive alternating up-steps and down-steps.
observations => { 1, 3, 4, 2, 7, 5, 6, 8, 1, 2, 4 }
Output from 1st .Zip() => { 1, 1, -1, 1, -1, 1, 1, -1, 1, 1 }
Output from 2nd .Zip() => { 1, -1, -1, -1, -1, -1, 1, 1, 1, -1 }
The line var runs = FindEqualRuns(consecutivePairs).Select(t => new Tuple<int, int>(t.Item1, t.Item2 + 1)).ToList(); then uses another function to find all the consecutive runs or either +1, -1, or 0 in that set, giving the startIndex and count. Because this gives us runs against the pairs enumeration (which is one element shorter than the original observations), we do need to add 1 to each count. For example, if it finds a run of 3 elements in the pairs enumeration, this represents a run of 4 elements in observations.
The output in runs gives the detail on the start and length of all the alternating sequences in the data.
runs => { {0, 2}, {1, 6}, {6, 4}, {9, 2} }
So there is:
a sequence of length 2, starting at the 0th index => { 1, 3 }
a sequence of length 6, starting at the 1st index => { 3, 4, 2, 7, 5, 6 }
a sequence of length 4, starting at the 6th index => { 6, 8, 1, 2 }
a sequence of length 2, starting at the 9th index => { 2, 4 }
Hope this helps

How to get all indices of the highest number in an array? [duplicate]

This question already has answers here:
c# Array.FindAllIndexOf which FindAll IndexOf
(10 answers)
Closed 5 years ago.
For example, I have an array
arr[5] = {1, 5, 2, 3, 5}
and the highest number is obviously 5.
My question is how do I get both the indices of the highest number (which is 5).
The expected result is 1 and 4.
var arr = new int[] { 1, 5, 2, 3, 5 };
int max = arr.Max();
List<int> indexes = new List<int>();
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == max)
indexes.Add(i);
}
int highindex = indexes.LastOrDefault();
Thats how you get all indexes of your highest number
var numbers = new int[] { 1, 5, 2, 3, 5 };
int max = numbers.Max();
var indexes = numbers.Select((c, i) => new
{
character = c, index = i
})
.Where(list => list.character == max)
.ToList();
You can use a LINQ query to find the numbers and index that equal the maximum number :
var arr=new[] {1, 5, 2, 3, 5};
var max = arr.Max();
var indexes= arr.Select( (n,idx)=>n==max?idx:-1)
.Where(idx=>idx!=-1)
.ToArray();
This will return {1,4}.
This query uses the Enumerable.Select overload that provides the index of the current element and returns that index if the number is equal to the maximum.
The original title was a bit confusing - how to find the two largest values. You can use a similar query to get the N largest values by selecting the value and index, ordering the results and taking first N items:
var indexes = arr.Select( (val,idx)=> (val:val,idx:idx) )
.OrderByDescending(p=>p.val)
.Take(2)
.Select(p=>p.idx);
This query uses C# 7 tuples to hold the intermediate results and give them a name

To find a sequence of one array into another array [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have two array objects
int[] arr1= new int[] {1,2,5,6,7,9,3,5,6,7}
int[] arr2 = new int[] {5,6,7}
Now, how to find the no of occurrences of arr2 in arr1?
Perhaps not very elegant, but it should work.
This selects all subarrays in a having the same length of b, and checks how many are equal to b.
int[] a = {1, 2, 3, 4, 5};
int[] b = {2, 3};
int count = 0;
int bl = b.Length;
for (int i = 0; i <= a.Length - bl; i++)
{
var suba = a.Skip(i).Take(bl);
if (suba.SequenceEqual(b))
count++;
}
N.B.: this solution considers overlapping subarrays, therefore if a = {2, 2, 2} and b = {2, 2}, the count will be 2.
you may use arr2.Intersect(arr1).Count()
So in your case it will return 3, as 3 elements in arr2 are present in arr1.
If this is not what you're asking for, please clarify.
Use the intersect. This code snippet will solve your problem and print all duplicates, and show the count of each in arr1. Note that I am also using the Linq Distinct(), so that when looping common occurrences, I only check once and not more.
int[] arr1= new int[] {1,2,5,6,7,9,3,5,6,7};
int[] arr2 = new int[] {5,6,7};
var listCommon = arr1.AsEnumerable().Where(arr2.AsEnumerable().Contains);
foreach (var x in listCommon.Distinct()) {
var numberOfOccurencesInArr1 = arr1.Where(y => y == x).Count();
Console.WriteLine(x + " is : " + numberOfOccurencesInArr1.ToString() + " times in arr1");
}
Console.ReadLine();
See MSDN for more information; http://msdn.microsoft.com/en-us/library/system.linq.enumerable.intersect(v=vs.110).aspx
The listCommon will be the number of common items in both arrays.
Try This
string result = string.Empty; ;
int[] arr1 = new int[] { 1, 2, 5, 6, 7, 9, 3, 5, 6, 7 };
int[] arr2 = new int[] { 5, 6, 7 };
int count = arr2.Intersect(arr1).Count();
if (count == arr2.Length)
{
result = "Found";
}
else
{
result = "Not Found";
}
If you want to count how many occurences of an integer of the second array are present in the first array then you could write
int[] arr1 = new int[] {1,2,5,6,7,9,3,5,6,7};
int[] arr2 = new int[] {5,6,7};
Dictionary<int, int> counter = new Dictionary<int, int>();
foreach(int x in arr1)
{
if(arr2.Contains(x))
{
if(counter.ContainsKey(x))
counter[x]++;
else
counter[x] = 1;
}
}
foreach(KeyValuePair<int, int> kvp in counter)
Console.WriteLine("Key=" + kvp.Key.ToString() + " is present " + kvp.Value.ToString() + " times");
int[] arr1 = new int[] { 1, 2, 5, 6, 7, 9, 3, 5, 6, 7 };
int[] arr2 = new int[] { 5, 6, 7 };
how to find the no of occurrences of arr2 in arr1?
If you are expecting the result to be 2. Since the 5,6,7 appears twice in the arr1
try this
var res = arr1.Where(x => arr2.Contains(x)).Count()/arr2.Count();
Try this one:
var results = (from a1 in arr1
join a2 in arr2
on a1 equals a2
group arr1 by a1 into Group
select new
{
Number = Group.Key,
Times = Group.Count()
});
foreach(var result in results)
Console.WriteLine(result.Number+" "+result.Times);
Please check this solution using the following fiddle .NET fiddle
You can use ToLookup on the ints in the first count which are also in the second array. Then you just have to take the min-count of all groups since that's the greatest intersection:
var subsetGroups = arr1.Where(i1 => arr2.Contains(i1)).ToLookup(i => i);
int minGroupCount = 0;
// check if all integers from the array are in the first at all
if(arr2.All(i => subsetGroups.Contains(i)))
{
minGroupCount = subsetGroups.Min(g => g.Count()); // 2
}
Note that this approach doesn't care about the order and it also doesn't care about the number of duplicates in the second array. This might be desired or not.
As you input is arrays you can use indexing to effectively count the number of occurrences of the subarrays in the main array:
var count = 0;
for (var i = 0; i < arr1.Length - arr2.Length + 1; i += 1) {
if (arr1[i] != arr2[0])
continue;
var isSubarray = true;
for (var j = 0; j < arr2.Length; ++j)
if (arr1[i + j] != arr2[j]) {
isSubarray = false;
break;
}
if (isSubarray)
count += 1;
}
The result will be 2 because 567 is found two times in 1256793567.
If the subarray can "overlap" itself (e.g. 11 in 111) all "overlaps" will be counted (e.g. the result will be 2 for this example). If that is not the intention you simply have to advance the index i at the end of the main loop to skip the found subarray.

Finding the last index of an array

How do you retrieve the last element of an array in C#?
LINQ provides Last():
csharp> int[] nums = {1,2,3,4,5};
csharp> nums.Last();
5
This is handy when you don't want to make a variable unnecessarily.
string lastName = "Abraham Lincoln".Split().Last();
With C# 8:
int[] array = { 1, 3, 5 };
var lastItem = array[^1]; // 5
The array has a Length property that will give you the length of the array. Since the array indices are zero-based, the last item will be at Length - 1.
string[] items = GetAllItems();
string lastItem = items[items.Length - 1];
int arrayLength = array.Length;
When declaring an array in C#, the number you give is the length of the array:
string[] items = new string[5]; // five items, index ranging from 0 to 4.
New in C# 8.0 you can use the so-called "hat" (^) operator! This is useful for when you want to do something in one line!
var mystr = "Hello World!";
var lastword = mystr.Split(" ")[^1];
Console.WriteLine(lastword);
// World!
instead of the old way:
var mystr = "Hello World";
var split = mystr.Split(" ");
var lastword = split[split.Length - 1];
Console.WriteLine(lastword);
// World!
It doesn't save much space, but it looks much clearer (maybe I only think this because I came from python?). This is also much better than calling a method like .Last() or .Reverse() Read more at MSDN
Edit: You can add this functionality to your class like so:
public class MyClass
{
public object this[Index indx]
{
get
{
// Do indexing here, this is just an example of the .IsFromEnd property
if (indx.IsFromEnd)
{
Console.WriteLine("Negative Index!")
}
else
{
Console.WriteLine("Positive Index!")
}
}
}
}
The Index.IsFromEnd will tell you if someone is using the 'hat' (^) operator
Use Array.GetUpperBound(0). Array.Length contains the number of items in the array, so reading Length -1 only works on the assumption that the array is zero based.
To compute the index of the last item:
int index = array.Length - 1;
Will get you -1 if the array is empty - you should treat it as a special case.
To access the last index:
array[array.Length - 1] = ...
or
... = array[array.Length - 1]
will cause an exception if the array is actually empty (Length is 0).
Also, starting with .NET Core 3.0 (and .NET Standard 2.1) (C# 8) you can use Index type to keep array's indexes from end:
var lastElementIndexInAnyArraySize = ^1;
var lastElement = array[lastElementIndexInAnyArraySize];
You can use this index to get last array value in any length of array. For example:
var firstArray = new[] {0, 1, 1, 2, 2};
var secondArray = new[] {3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5};
var index = ^1;
var firstArrayLastValue = firstArray[index]; // 2
var secondArrayLastValue = secondArray[index]; // 5
For more information check documentation
The following will return NULL if the array is empty, else the last element.
var item = (arr.Length == 0) ? null : arr[arr.Length - 1]
say your array is called arr
do
arr[arr.Length - 1]
Is this worth mentioning?
var item = new Stack(arr).Pop();
Array starts from index 0 and ends at n-1.
static void Main(string[] args)
{
int[] arr = { 1, 2, 3, 4, 5 };
int length = arr.Length - 1; // starts from 0 to n-1
Console.WriteLine(length); // this will give the last index.
Console.Read();
}
static void Main(string[] args)
{
int size = 6;
int[] arr = new int[6] { 1, 2, 3, 4, 5, 6 };
for (int i = 0; i < size; i++)
{
Console.WriteLine("The last element is {0}", GetLastArrayIndex(arr));
Console.ReadLine();
}
}
//Get Last Index
static int GetLastArrayIndex(int[] arr)
{
try
{
int lastNum;
lastNum = arr.Length - 1;
return lastNum;
}
catch (Exception ex)
{
return 0;
}
}
This is simplest and works on all versions.
int[] array = { 1, 3, 5 };
int last = array[array.Length - 1];
Console.WriteLine(last);
// 5

Categories