LINQ to Get Closest Value? - c#

I have a List, MyStuff has a property of Type Float.
There are objects with property values of 10,20,22,30.
I need to write a query that finds the objects closest to 21, in this case it would find the 20 and 22 object. Then I need to write one that finds the object closes to 21 without going over, and it would return the object with a value of 20.
I have no idea where/how to begin with this one. Help?
Thanks.
Update - wow there are so many awesome responses here. Thanks! I don't know which one to follow so I will try them all. One thing that might make this more (or less) interesting is that the same query will have to apply to LINQ-to-SQL entities, so possibly the answer harvested from the MS Linq forums will work the best? Don't know.

Try sorting them by the absolute value of the difference between the number and 21 and then take the first item:
float closest = MyStuff
.Select (n => new { n, distance = Math.Abs (n - 21) })
.OrderBy (p => p.distance)
.First().n;
Or shorten it according to #Yuriy Faktorovich's comment:
float closest = MyStuff
.OrderBy(n => Math.Abs(n - 21))
.First();

Here's a solution that satisfies the second query in linear time:
var pivot = 21f;
var closestBelow = pivot - numbers.Where(n => n <= pivot)
.Min(n => pivot - n);
(Edited from 'above' to 'below' after clarification)
As for the first query, it would be easiest to use MoreLinq's MinBy extension:
var closest = numbers.MinBy(n => Math.Abs(pivot - n));
It's also possible to do it in standard LINQ in linear time, but with 2 passes of the source:
var minDistance = numbers.Min(n => Math.Abs(pivot - n));
var closest = numbers.First(n => Math.Abs(pivot - n) == minDistance);
If efficiency is not an issue, you could sort the sequence and pick the first value in O(n * log n) as others have posted.

Based on this post at the Microsoft Linq forums:
var numbers = new List<float> { 10f, 20f, 22f, 30f };
var target = 21f;
//gets single number which is closest
var closest = numbers.Select( n => new { n, distance = Math.Abs( n - target ) } )
.OrderBy( p => p.distance )
.First().n;
//get two closest
var take = 2;
var closests = numbers.Select( n => new { n, distance = Math.Abs( n - target ) } )
.OrderBy( p => p.distance )
.Select( p => p.n )
.Take( take );
//gets any that are within x of target
var within = 1;
var withins = numbers.Select( n => new { n, distance = Math.Abs( n - target ) } )
.Where( p => p.distance <= within )
.Select( p => p.n );

List<float> numbers = new List<float>() { 10f, 20f, 22f, 30f };
float pivot = 21f;
var result = numbers.Where(x => x >= pivot).OrderBy(x => x).FirstOrDefault();
OR
var result = (from n in numbers
where n>=pivot
orderby n
select n).FirstOrDefault();
and here comes an extension method:
public static T Closest<T,TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, TKey pivot) where TKey : IComparable<TKey>
{
return source.Where(x => pivot.CompareTo(keySelector(x)) <= 0).OrderBy(keySelector).FirstOrDefault();
}
Usage:
var result = numbers.Closest(n => n, pivot);

Related

Neo4j cypher transform query to c#

I need to transform this query to use with the graphclient inside c#:
MATCH p=(n)-[r*2..10]-(m)
WHERE n.Id = 94 and m.Id = 94
AND NONE (node IN NODES(p) WHERE SIZE(
FILTER(x IN NODES(p) WHERE node = x AND x.Id <> 94)
) > 1
)
RETURN EXTRACT(n IN NODES(p)| n.Id) AS Paths, length(p), r
order by length(p)
the closest solution that I could put together is:
var results = neoDB.Cypher
.Match("p=(n:JUNCTIONS)-[r*2..10]-(m:JUNCTIONS)")
.Where("n.Id={startNodeId} and m.Id={endNodeId}")
.WithParam("startNodeId", 92)
.WithParam("endNodeId", 92)
.Return((p, n, m, r) => new
{
path = p.CollectAs<Neo4jClient.ApiModels.Cypher.PathsResult>(),
node1 = n.As<cNode>(),
node2 = m.As<cNode>(),
Relation = r.As<IEnumerable<cNode>>()
}).Results;
the filter for lengths greater than one was unnecessary, this solution is good for me

Optimizing code for range selection

Given an array and two more arrays i need to find the range of elements in the first array
For e.g. MainArray={2,4,6,5,8,9}, range1={4,5,6}, range2={6,9,8}
for First-Iteration i have to select elements in MainArray in range [4,6] ->[4,6,5] --[3] is the output
for second-Iteration i have to select elements in MainArray in range [5,9] ->[5,8,9]--[3] is the output
for third-Iteration i have to select elements in MainArray in range [6,8] ->[6,8]--[2] is the output
array returned [3,3,2]
static void Main(string[] args)
{
var rng = new Random();
var result = processFunc(Enumerable.Range(0, 5000000).OrderBy(x => rng.Next()).ToArray(),
Enumerable.Range(0, 20000).OrderBy(x => rng.Next()).Take(200).ToArray(),
Enumerable.Range(0, 20000).OrderBy(x => rng.Next()).Take(200).ToArray());
}
public static int[] processFunc(int[] scores,int[] l,int[] r)
{
IList<int> output = new List<int>();
for (int i = 0; i < l.Length; i++)
{
var bestMatch = scores.Where(x => x >= l[i] && x <= r[i]);
output.Add(bestMatch.Count());
}
return output.ToArray();
}
The code runs fine when numbers are small but once they >50,000 the program becomes slow. How can I optimize this solution ?
Assuming l and r have the same length, consider this approach:
public static int[] processFunc(int[] scores, int[] l, int[] r)
{
var min = Math.Min(l.Min(z => z), r.Min(z => z));
var max = Math.Max(l.Max(z => z), r.Max(z => z));
var grouped = scores.Where(z => z >= min && z <= max).GroupBy(z => z).Select(val => Tuple.Create(val.Key, val.Count())).OrderBy(z => z.Item1).ToList();
return l.Zip(r, (left, right) =>
{
var matching = grouped.Where(z => z.Item1 >= left).TakeWhile(z => z.Item1 <= right);
return matching.Sum(z => z.Item2);
}).ToArray();
}
min and max are used to ignore irrelevant (too large or too small) numbers. grouped is used to pre-calculate the counts and put them in order. Zip is used to line up the l and r values and sum the counts together.
This solution is roughly 2-3 times faster on my machine than the original code (and most of the remaining time being spent is actually setting up the parameters, rather than in the function itself).

LINQ orderby from specific value

Sample:
var aux = new int[] { -1,0,1,-1,2,3,4,5,6,7 }
Expected result:
{ 2,3,4,5,6,7,1,0,-1,-1 }
How?
--
Edit: Sorry for the poor question.
I want to order all from the value 2, and others who are put in the lower end (I fixed the text).
var query = aux
.OrderBy(i => i < 2 ? 2 : 1) //small numbers last
.ThenBy(i => i < 2 ? -i : i); //large numbers asc, small numbers desc
var res = aux.Where(a => a>=2 ).OrderBy (a => a)
.Concat(aux.Where (a => a <2).OrderByDescending (a => a))

find the first available long in a List<long>

ok, this should be interesting.
lets assume i have the following code:
in this example, the first available number would be 2.
List<long> myList = new List<long>(){0,1,10,3};
in this example, the first available number would be '4'.
List<long> myList = new List<long>(){0,1,2,3};
any ideas?
So by "available" you mean "the lowest non-negative number which doesn't already exist in the list"?
I'd be tempted to write something like:
HashSet<long> existing = new HashSet<long>(list);
for (long x = 0; x < long.MaxValue; x++)
{
if (!existing.Contains(x))
{
return x;
}
}
throw new InvalidOperationException("Somehow the list is enormous...");
EDIT: Alternatively, you could order the list and then find the first value where the index isn't the same as the value...
var ordered = list.OrderBy(x => x);
var differences = ordered.Select((value, index) => new { value, index })
.Where(pair => pair.value != pair.index)
.Select(pair => (int?) pair.index);
var firstDifference = differences.FirstOrDefault();
long nextAvailable = firstDifference ?? list.Count;
The last line is to take care of the situation where the list is contiguous from 0. Another alternative would be:
var nextAvailable = list.Concat(new[] { long.MaxValue })
.OrderBy(x => x)
.Select((value, index) => new { value, index })
.Where(pair => pair.value != pair.index)
.Select(pair => pair.index)
.First();
This should be fine so long as the list doesn't contain long.MaxValue + 1 elements, which it can't in current versions of .NET. (That's a lot of memory...) To be honest, this will already have problems when it goes beyond int.MaxValue elements due to the Select part taking an int index...
list.Sort();
var range = Enumerable.Range( list.First(), list.Last()- list.First());
var number = range.Except(list).FirstOrDefault();

Finding if a target number is the sum of two numbers in an array via LINQ

A basic solution would look like this:
bool sortTest(int[] numbers, int target)
{
Array.Sort(numbers);
for(int i = 0; i < numbers.Length; i++)
{
for(int j = numbers.Length-1; j > i; j--)
{
if(numbers[i] + numbers[j] == target)
return true;
}
}
return false;
}
Now I'm very new to LINQ but this is what I have written so far:
var result = from num in numbers
where numbers.Contains(target -num)
select num;
if (result.Count() > 0)
return true;
return false;
Now i'm running into an issue given the following example:
Array: 1, 2, 4, 5, 8
Target: 16
It should return back false, but it's catching 16-8=8. So how do I go about not letting it notice itself in the contains check? Or can I make a second array each time within the query that doesn't contain the number I'm working with(thus solving the problem)?
Thanks in advance.
Is this what you're looking for?
var result = from n1 in numbers
from n2 in numbers
where n1 != n2 && n1 + n2 == target
select new { n1, n2 };
[Edit]
This returns matches twice and ignores the situation where a number is duplicated in the array. You can't handle these situations using Expression Syntax because you can't access the index of a matched item, but you can do it like this:
var result = numbers.Select((n1, idx) =>
new {n1, n2 = numbers.Take(idx).FirstOrDefault(
n2 => n1 + n2 == target)}).Where(pair => pair.n2 != 0);
As long as you don't have any zeros in your array.
[Further thought Edit]
The perfect mix solution:
var result = from item in numbers.Select((n1, idx) =>
new {n1, shortList = numbers.Take(idx)})
from n2 in item.shortList
where item.n1 + n2 == target
select new {n1 = item.n1, n2};
What I'd do to solve this problem in general is first write a "chooser".
public static IEnumerable<IEnumerable<T>> Chooser<T>(this IList<T> sequence, int num)
{ ... left as an exercise ... }
The output of the chooser is a sequence of sequences. Each sub-sequence is of length num, and consists of elements chosen from the original sequence. So if you passed { 10, 30, 20, 50 } as the sequence and 3 for num, you'd get the sequence of sequences:
{10, 30, 20}, {10, 30, 50}, {10, 20, 50}, {30, 20, 50}
as a result.
Once you've written Chooser, the problem becomes easy:
var results =
from subsequence in numbers.Chooser(2)
where subsequence.Sum() == target
select subsequence;
And now you can solve the problem for subsequences of other sizes, not just pairs.
Writing Chooser is a bit tricky but it's not too hard.
To improve on pdr's reply and address the concerns mentioned in the comments you could use the overloaded Select method to compare the indices of the items and ensure uniqueness.
public bool sortTest(int[] numbers, int target)
{
var indexedInput = numbers.Select((n, i) => new { Number = n, Index = i });
var result = from x in indexedInput
from y in indexedInput
where x.Index != y.Index
select x.Number + y.Number == target;
return result.Any(item => item);
}
Or in dot notation:
var result = numbers.Select((n, i) => new { Number = n, Index = i })
.SelectMany(
x => indexedInput,
(x, y) => new { x = x, y = y })
.Where(item => item.x.Index != item.y.Index)
.Select(item => item.x.Number + item.y.Number == target);

Categories

Resources