Is it possible to express this code in LINQ? - c#

I'm reading a C# book for beginners, and in every end of the chapter, there are exercises to be answered based on the lessons tackled.
One of those exercises goes this way: (not the exact wordings)
Write a program that will accept an int as the array length, and the values for the array.
Then will print:
"0" if the array is not sorted in ascending way.
"1" if it is sorted. And,
"2" if it is sorted, but there are duplicates.
Example:
// Sorted
Input: 1, 2, 3, 5
Print: 1
// Not sorted
Input: 2, 1, 3, 6
Print: 0
// Sorted, but with duplicates
Input: 2, 2, 3, 7
Print: 2
I don't know if my logic here is absolute, but somehow it is working,
and I done it in my way using this code:
int arrayLength = 0;
int prev, next;
int sortStatus = 1;
Console.Write("Input array Length: ");
arrayLength = Convert.ToInt32(Console.ReadLine());
int[] ar = new int[arrayLength];
for (int x = 0; x < arrayLength; x++)
{
Console.Write("Input {0} value: ", (x+1).ToString());
ar[x] = Convert.ToInt32(Console.ReadLine());
}
for (int x = 0; x < ar.Length-1; x++)
{
prev = (int)ar[x];
next = (int)ar[x + 1];
if (next < prev)
sortStatus = 0;
if (next == prev)
sortStatus = 2;
}
Console.Write(sortStatus.ToString());
Console.Read();
Is it possible to express this in LINQ? How?

if (ar.SequenceEqual(ar.OrderBy(x => x)))
{
if (ar.Distinct().Count() == ar.Length)
return 1;
else
return 2;
}
else
{
return 0;
}

A pure LINQ alternative ... (for academic interest only (but probably still faster than the accepted answer!)
var input = new int[] { 1, 2, 3, 4, 5 };
var output = input.Zip(input.Skip(1), (a, b) => new {a=a, b=b})
.Aggregate(1, (status, x) => status == 0 ? 0 : ((x.a > x.b ? 0 : (x.a == x.b ? 2 : status))));

As a note, your expressed non-LINQ logic has a flaw.
if (next < prev)
sortStatus = 0;
if (next == prev)
sortStatus = 2;
Your rule says that the array must be sorted ascending but have duplicates in order to get an output of 2. However, your logic will return 2 for { 1, 9, 7, 7 }.
Another way to write your code might be the following. (This is not using LINQ, but this is too long to post as a comment to your question.)
static int EvaluateArray(int[] array)
{
int? lastItem = null;
bool match = false;
foreach (int item in array)
{
if (item < lastItem)
return 0;
else if (item == lastItem)
match = true;
lastItem = item;
}
if (match)
return 2;
return 1;
}
In this method, we will early-return as soon as we have an item less than the previous item. Otherwise, we will set a boolean if we come across a matching value. At the end of the loop, we know the array is sorted ascending. The only thing left is check if there was a match.

Untested.
IEnumerable<int> signs =
from i in Enumerable.Range(0, ar.Length).Skip(1)
select ar[i-1].CompareTo(ar[i]);
int result =
signs.Any(sign => sign < 0) ? 0 :
signs.All(sign => 0 < sign) ? 1 :
2;
Also untested:
int minSign = !ar.Skip(1).Any() ? 1 :
(
from i in Enumerable.Range(0, ar.Length).Skip(1)
select ar[i-1].CompareTo(ar[i])
).TakeWhile(x => 0 <= x).Min();
int result =
minSign < 0 ? 0 :
0 < minSign ? 1 :
2;

Related

How to remove two elements of collection at the same time? Task about cyclopes lenses

I cant solve this task.
There are N cyclopes and an array of N elements.
Every element is eyesight value of single cyclop.
Every cyclop needs a lens with a value K but he will be okay with
lens of value K+1 or K-1.
Cyclopes always buy lenses in pairs.
For example 5 cyclopes with eyesight values [1,-1,2,3,-3] will need to buy 3 pairs of lenses.
I need to write a program that will count minimal amount of lens pairs need.
I tried it like this
int cyclops = 4;
int[] cyclopsSightValues = { 1, 7, 4, 1 };
if (cyclops < 2) { return 1;}
List<int> list = cyclopsSightValues .ToList();
int matchCount = 0;
for (int i = 0; i < list.Count; i++)
{
for (int j = 0; j < list.Count; j++)
{
if (list[i] == list[j] ||
list[i] + 1 == list[j] ||
list[i] + 2 == list[j] ||
list[i] - 1 == list[j] ||
list[i] - 2 == list[j])
{
int valueToRemove1 = list[i];
int valueToRemove2 = list[j];
list.Remove(valueToRemove1);
list.Remove(valueToRemove2);
matchCount++;
continue;
}
}
}
return matchCount + (cyclops-matchCount*2);
I think i need to find matching eyesights and remove them from list, but the result always comes out less then the correct one by 1.
Maybe my logic is wrong altogether?
Any help would be much appreciated.
Look, if two cyclops have eyesight difference 2 or less by absolute value they can buy lenses which fit both of them, e.g.
3 and 1 can buy pair of 2 lenses. Let's try to use greedy approach now: order cyclops by their eye sights and try
to use spare lenses as frequent as we could:
1, -1, 2, 3, -3 -> -3, -1, 1, 2, 3
-3 v -1, 1 v 2, 3
can use
-2 1
So far so good all we have to do is to sort and scan:
private static int Solve(int[] cyclopsSightValues) {
Array.Sort(cyclopsSightValues);
int result = 0;
bool hasSpare = false;
for (int i = 0; i < cyclopsSightValues.Length; ++i)
if (hasSpare && cyclopsSightValues[i - 1] + 2 >= cyclopsSightValues[i])
hasSpare = false; // We use spare lense from the previous cyclope
else {
// we have to buy a pair, and now we have a spare lense
hasSpare = true;
result += 1;
}
return result;
}
Demo:
int[][] tests = {
new[] { 1, -1, 2, 3, -3 },
new int[] { },
new[] { 1, 1, 1, 1 },
};
string report = string.Join(Environment.NewLine, tests
.Select(item => $"[{string.Join(", ", item)}] => {Solve(item)}"));
Console.Write(report);
Output:
[1, -1, 2, 3, -3] => 3
[] => 0
[1, 1, 1, 1] => 2
Please, fiddle yourself
Untested, but should more or less work:
public static IEnumerable<int> LensesToBuy(IEnumerable<int> cyclopsVisions)
{
int? buf = null;
var e = cyclopsVisions.OrderBy(v => v).GetEnumerator();
while(e.MoveNext())
{
if (!buf.HasValue)
{
buf = e.Current;
}
else if (Math.Abs(buf.Value - e.Current) > 2)
{ // cached value not compatible
yield return buf.Value;
buf = e.Current;
}
else
{ // cached value is compatible
if (buf.Value == e.Current) yield return buf.Value;
if (buf.Value > e.Current) yield return buf.Value - 1;
if (buf.Value < e.Current) yield return buf.Value + 1;
buf = null;
}
}
if (buf.HasValue) yield return buf.Value;
}
Call it like this:
int[] cyclopsSightValues = { 1, 7, 4, 1 };
var result = LensesToBuy(cyclopsSightValues); //okay to pass array
Console.WriteLine(result.Count());

How to get the absolute sum of all negative value in a specific range from list in c#

I have a space separated list of int like below:
1 -2 3 1 -8 3 -2 5
I need an absolute sum of a specific range of that list i.e. from 4th index to 6th index. The result will be 10(from abs(-8-2)).
I can get the perfect result by using below code:
List<int> _subList = new List<int>();
string _input = Console.ReadLine();
List<int> _list = _input.Split(' ').Select(n => Convert.ToInt32(n)).ToList<int>();
var k =Math.Abs(_list.GetRange(3, 3).Where(x=>x<0).Sum());
Console.WriteLine(k);
But is there any way to do it without using Linq?
But is there any way to do it without using Linq?
List<int> data = new List<int>() { 1, -2, 3, 1, -8, 3, -2, 5 };
long sum = 0;
var minIndex = 4;
var maxIndex = 6;
for (int i = minIndex; i <= maxIndex && i < data.Count; i++)
{
if (data[i] < 0)
{
sum -= data[i];
}
}
Above is using a for loop with the appropriate indices to get the values out, and using - rather than + and Abs to get the output you expect.
If you explicitly need the string parsing:
string input = Console.ReadLine();
var data = input?.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? Array.Empty<string>();
long sum = 0;
var minIndex = 4;
var maxIndex = 6;
for (int i = minIndex; i <= maxIndex && i < data.Length; i++)
{
if (int.TryParse(data[i], out var val) && val < 0)
{
sum -= val;
}
}
Note you need to use ?. with the Split to handle Console.ReadLine returning null. And StringSplitOptions simplifies the trimming of whitespace.
This is a great use case for LINQ extension methods.
split the string to get the constituent integer strings
skip to start offset
take desired element count (endOffset - startOffset + 1)
convert string values to integers via projection
filter for negative values
project negation
aggregate the sum
.
string input = "...";
var startOffset = 4;
var endOffset = 6;
var absSum = input.Split(' ')
.Skip(startOffset)
.Take(endOffset - startOffset + 1)
.Select(s => Convert.ToInt32(s))
.Where(i => 0 > i)
.Select(i => -i)
.Sum();
Without LINQ extension methods:
var targetElements = input.Split(' ')
//.Skip(startOffset).Task(endOffset - startOffset + 1)
.AsSpan(startOffset, endOffset - startOffset + 1);
var absSum = 0;
foreach(var element in targetElements)
{
//.Select(s => Convert.ToInt32(s))
var i = Convert.ToInt32(element);
//.Where(i => 0 > i)
if( 0 > i )
{
//.Sum()
absSum += i;
}
}
// we know sum is negative so negate for absolute value
absSum = -absSum;
List<int> _subList = new List<int>();//1
string _input = Console.ReadLine();//2
List<int> _list = _input.Split(' ')//3
.Select(n => Convert.ToInt32(n))//4
.ToList<int>();//5
var k =Math.Abs(_list.GetRange(3, 3)//6
.Where(x=>x<0).Sum());//7
Console.WriteLine(k);
I think code below is right
In //1 creates empty List
In //2 read space as string
In //3 getting numbers to int List
In //4 Iterating and converting to numbers from string to int
In //5 get All as List in type int
In //6 I think Math.abs clear,GetRange(3,3) is same begin from range in math (3,3+3] thats 4,5,6
In //7 Sum() for getting sum
List<int> _subList = new List<int>();
string _input = Console.ReadLine();
List<int> _list = _input.Split(' ').Select(n => Convert.ToInt32(n)).ToList<int>();
var k =Math.Abs(_list.GetRange(3, 3).Where(x=>x<0).Sum());
Console.WriteLine(k);
Here _list.GetRange(3, 3) will create the sublist
and then Where(x=>x<0).Sum() pick the negative values and sum it up
and ulimately Math.Abs() will give the absolute value
But here i have used System.Linq; If anyone can provide with better solution other than manual looping would be appriciated

C# problems getting results from List<T> BinarySearch with duplicates in the first and last position

I'm playing around with different implementations for the most simple of the LeetCode problems TwoSums. I have various other ways working fine using methods like indexof, Dictionary searches, brute force, etc... and my best so far is better than 98.67% using List.IndexOf
I'm trying to implement a version with BinarySearch for comparison. It passes most tests but seems to fail when needing to sum duplicates in the first and last position and the list length is gt 2. I'm sure there are other fail conditions but I can't get past this one.
When stepping through the code it returns a negative number instead of the index when the last and first are the same value despite starting at i+1 index location.
I have to be missing something obvious but I'm just not seeing it for some reason. Maybe I'm just misunderstanding how BinarySearch works with index and length.
These pass:
{ 0, 2, 3 } target 3
result: {0, 2}
{ 3, 3 } target 6
result: { 0, 1 }
{ 0, 2, 0, 3 } target 0
result: { 0, 2 }
These fail:
{ 0, 2, 0 } target 0
expected: { 0, 2 }
result: null
{ 1, 4, 1 } target 2
expected: { 0, 2 }
result: null
The code sample is verbose for now while I'm working through the issues. I'll minimize it later.
offset is used to start the search at an index higher than i
subsetLength is used to keep the search length from going outside the bounds
The n > i is just a sanity check to make sure n is a higher index value than i before returning a valid result
public int[] TwoSum(int[] nums, int target)
{
List<int> numList = new List<int>(nums);
for (int i = 0; i < nums.Length; i++)
{
int offset = i + 1;
int subsetLength = nums.Length - offset;
int searchNum = target - nums[i];
int n = numList.BinarySearch(offset, subsetLength, searchNum, null);
if (n > i)
return new int[] { i, n };
}
return null;
}
Yes, you have a special case target / 2 + target / 2 == target when you check for two items each of which are target / 2:
public int[] TwoSum(int[] nums, int target) {
// Comment out if nums is already sorted
Array.Sort(nums);
for (int i = 0; i < nums.Length; ++i) {
int item = nums[i];
int toFind = target - item;
if (toFind < nums[i])
break;
int index = Array.BinarySearch(nums, toFind);
if (index >= 0) {
if (toFind == item) {
// Special case: two equal numbers: target / 2 + target / 2 = target
if (i < nums.Length - 1 && nums[i] == nums[i + 1])
return new int[] { i, i + 1 };
break;
}
else
return new int[] { i, index };
}
}
return new int[] { -1, -1 };
}
Just sort the list before searching it:
public int[] TwoSum(int[] nums, int target)
{
List<int> numList = new List<int>(nums);
numList.Sort();
for (int i = 0; i < nums.Length; i++)
{
int offset = i + 1;
int subsetLength = nums.Length - offset;
int searchNum = target - nums[i];
int n = numList.BinarySearch(offset, subsetLength, searchNum, null);
if (n > i)
return new int[] { i, n };
}
return null;
}

Find the next closest element in a List<long> when list has some duplicate elemets

I have a SortedList with long datatype elements where the elements
may repeat ( has some duplicate records too).
Eg: ValueCov = { 1,2,2,2,5,5,5,5,6,6,7,7,7,...........}
I have to search a value "x" each time in this list with the option
BinarySearch(x) in C# and when the value "x" is not there in the
ValueCov list, I need to find the next closer value's index. (least
diff from value "x" ).
The issue I have here is, as duplicate elements available if I search
for value x=3, This should return value=2's index from valueCov (
Index=1) . But the second time I search for value=3 this shouldn't
return the previously returned index because I'm fixing that
position for some other work. So I need the index as = 2 when I search
the second time.
Please provide a solution for this search while duplicates available in a list.
for (int i = 0; i < valueWat.Count; i++)
{
var x=valueWat[i];
int index = valueCov.BinarySearch(x);
//Console.Write(index + ",");
if (index >= 0)
{
//Console.Write(sortedCov[index].Key + ",");
Console.WriteLine("addr of w/m" + sortedWat[i].Key);
//adding addr of w/m and coveraddr
blockedWaterCoverMap.Add(sortedCov[index].Key, sortedWat[i].Key);
//valueCov.Remove(x);
//valueCov[index] = 10000000000;
valueCov[index] = -100000000;
// valueWat[i] = 20000000000;
//foreach (var z in blockedWaterCoverMap)
//{
// Console.WriteLine("cov,wat indexpair" + z.Key+z.Value);
//}
}
else
{
int ind = findClosest(x);
Console.WriteLine("index,value#index\t" + ind + "\t" + valueCov[ind]);
blockedWaterCoverMap.Add(sortedCov[ind].Key, sortedWat[i].Key);
valueCov[ind] = 00000; valueCov[index] = 00000;
}
}
/////////
private int findClosest(long data)
{
int i = 0; // index of currently checked element from valueCov
int ind =i; // returned index of the closest element
// current lowest distance to searched value:
long min = long.MaxValue;
// currently counted difference between input value
// and the next element of the list valueCov
long diff = 0;
var valueCov = new List<long>();
foreach (var y in sortedCov)
{
valueCov.Add(y.Value);
}
for ( i = 0; i < valueCov.Count; i++)
{
var x=valueCov[i];
if ((diff = Math.Abs(x - data)) < min)
{
min = diff;
ind = i; // the searched index is updated
//Console.WriteLine("findclosest i\t" + i);
}
}
// random selection of the index from the closest
// found values
List<int> indices = new List<int>();
for (int n = 0; n < valueCov.Count; n++)
{
if (valueCov[n] == valueCov[ind])
indices.Add(n);
}
Random r = new Random();
ind = indices[r.Next(indices.Count)];
return ind;
}
I would just use linq to get the value, and you can later get the index by BinarySearch
public static void Main()
{
var valueCov = new long[]{ 1,2,2,2,5,5,5,5,6,6,7,7,7};
var result = 0L;
var x = 3;
result = FindNextCloseset(valueCov, x, result);
Console.WriteLine(result);
result = FindNextCloseset(valueCov, x, result);
Console.WriteLine(result);
}
public static long FindNextCloseset(long[] values, int occurrence, long searchAfterValue)
{
return values
.Where(i => i > searchAfterValue)
.GroupBy(i => i)
.Where(i => i.Count() == occurrence)
.Select(i => i.Key)
.FirstOrDefault();
}
Fiddle
Here is a solution that I came up with that you can start with. It is long, and you should create unit tests and refactor it since it contains a lot of logic:
public static int FindIndexOfClosest(List<long> list, long value_to_search_for, int time)
{
int index = list.BinarySearch(value_to_search_for);
if (index >= 0) //We found the value
{
while (index > 0 && list[index - 1] == value_to_search_for)
//BinarySearch might not return the index of the first occurance
{
index--;
}
index += time;
if (index >= list.Count || list[index] != value_to_search_for)
return -1;
return index;
}
else
{
int location_for_next_larger_number = ~index; //This could be equal to Count
int? larger_index = location_for_next_larger_number == list.Count
? (int?) null
: location_for_next_larger_number;
int? smaller_index = null;
if (!larger_index.HasValue)
{
if (list.Count > 0)
smaller_index = list.Count - 1;
}
else
{
int i = location_for_next_larger_number;
while (i > 0 && list[i - 1] == larger_index.Value)
i--;
if (i > 0)
smaller_index = i - 1;
}
int? closer_number_index = null;
if (larger_index.HasValue)
closer_number_index = larger_index.Value;
if (smaller_index.HasValue)
{
if (!closer_number_index.HasValue)
closer_number_index = smaller_index.Value;
else
{
if (Math.Abs(list[smaller_index.Value] - value_to_search_for) < Math.Abs(list[closer_number_index.Value] - value_to_search_for))
closer_number_index = smaller_index.Value;
}
}
if (closer_number_index.HasValue)
{
while (closer_number_index > 0 && list[closer_number_index.Value - 1] == list[closer_number_index.Value])
closer_number_index = closer_number_index.Value - 1;
long closer_number_value = list[closer_number_index.Value];
closer_number_index += time;
if (closer_number_index.Value >= list.Count || list[closer_number_index.Value] != closer_number_value)
return -1;
return closer_number_index.Value;
}
return -1;
}
}
And you can test it like this:
static void Main(string[] args)
{
List<long> list = new List<long> {1, 2, 2, 2, 5, 5, 5, 5, 6, 6, 7, 7, 7};
var test1 = FindIndexOfClosest(list, 3, 0); // returns 1
var test2 = FindIndexOfClosest(list, 3, 1); // returns 2
var test3 = FindIndexOfClosest(list, 3, 2); // returns 3
var test4 = FindIndexOfClosest(list, 3, 3); // returns -1 (not found)
}
Make sure that the list that you give to the FindIndexOfClosest method is always sorted.
Take a look at the reference for the BinarySearch method as it will help you understand the code I provided.

Is it possible to find if a combination of an array the size of 5 can make exactly 15?

I'm kind of new to programming, I have gotten values from an array the size of 5.
From this array, I need to see if any combination of 3 of those 5 numbers can make 15, is there any way this is possible?
Ex. Array[2] + Array[4] + Array[1] = 15 even though there are 5 numbers within the array
Here is how I would go about it:
boolean sum15( int Array[5] ) {
bool isUsed[5] = {0}; // Prevents duplicates
for( int i = 0; i < 5; i++ ) // Nested for loops iterate through every combo
{
isUsed[i] = true; // This makes it so the next loop doesn't use Array[i]
for( int j = 0; j < 5; j++ )
{
if( isUsed[j] ) // Check if Array[i] was already used
continue; // "Continue" restarts the loop, then increments 'j'
isUsed[j] = true;
for( int k = 0; k < 5; k++ )
{
if( isUsed[k] )
continue;
if( Array[i] + Array[j] + Array[k] == 15 ) // Check if valid combo
return true;
}
isUsed[j] = false;
}
isUsed[i] = false;
}
return false; // No match was found
}
"is there any way this is possible?"
Where there is a will there is a way. For all problems, there is code that can solve it.
Here's how I would do it:
var source = new [] { 2, 3, 5, 7, 11 };
var query =
from values in source.Selection(3)
where values.Sum() == 15
select values.ToArray();
I get this result:
Now, of course, you do need to define the Selection extension method. Its job is to select a list of all of the possible ways to select count number of elements from the source enumerable.
I defined it like this:
public static IEnumerable<IEnumerable<T>> Selection<T>(
this IEnumerable<T> source, int count)
{
return
source
.SelectMany((t, n) =>
count == 1
? new [] { new [] { t }.AsEnumerable() }.AsEnumerable()
: source
.Skip(n + 1)
.Selection(count - 1)
.Select(tt => new [] { t }.Concat(tt)));
}

Categories