Say I have an array with 60 elements, and I want to select 40 elements from this array. The subset should consist of elements with evenly spaced indices. In theory I would need to select every 1.5th point, which is obviously not possible.

I tried doing it with a recursive function, kind of a divide & conquer approach. It selects the point in the middle between two boundaries and adds it to the result, then it splits this range up into two sub-ranges and executes the function again for these ranges:

Private Sub AddMiddlePoint(ByRef data() As Double, ByRef Result As List(Of Double), Lowerindex As Integer, Upperindex As Integer, MaxVals As Integer, MinSize As Integer)
    Dim middle As Integer = CInt((Upperindex + Lowerindex) / 2)
    Dim newBorderLower1 As Integer = Lowerindex
    Dim newBorderUpper1 As Integer = middle
    Dim newBorderLower2 As Integer = middle + 1
    Dim newBorderUpper2 As Integer = Upperindex
    If Result.Count < MaxVals Then
        If newBorderUpper1 - newBorderLower1 > MinSize Then AddMiddlePoint(data, Result, newBorderLower1, newBorderUpper1, MaxVals, MinSize)
        If newBorderUpper2 - newBorderLower2 > MinSize Then AddMiddlePoint(data, Result, newBorderLower2, newBorderUpper2, MaxVals, MinSize)
    End If
End Sub

I call this function like this:

Private Function ThinOutData(source() As Double, Percentage As Integer) As Double()
    Dim newData As New List(Of Double)
    Dim amount As Integer = CInt(Percentage / 100 * source.Count)
    AddMiddlePoint(source, newData, 0, source.Count - 1, amount, CInt(Int(1 / Percentage) + 1))
    Return newData.ToArray
End Function

It basically works, but the subset that is selected isn't very evenly spaced. Some huge gaps, some small gaps and so on.

Does anyone have a suggestion how this can be improved? I am of course aware that you can't get perfectly even spacing for fractional steps, but my method returns uneven points even if it needs to pull every second or third value from the array.

My code is in VB.NET but feel free to use other languages that you are familiar with. It shouldn't be too hard to translate, I guess.

  • 4
    \$\begingroup\$ This question appears to be off-topic because it is about altering a core functionality of the code, and therefore the code is not considered to be "working" (as in "working as you want it to") \$\endgroup\$ – Simon Forsberg Apr 11 '14 at 14:15
  • \$\begingroup\$ That could be true, however in my opinion it was also working too well for Stack Overflow, so I used Code Review. \$\endgroup\$ – Jens Apr 11 '14 at 15:20

Your math was wrong, but don't worry it was a simple fix. And I have provided an easier way to do it on top of that.

The key to this review is that we're going to take advantage of the .Net libraries, which you should remember can practically do anything you want them to. That is IF you know how to use them.

in C#.Net

double percent = 40d / 60d;
var newValues = myList.Where((value, index) => index % (1 / percent) < percent).ToList();

in VB.Net

Dim percent As Double = 40.0 / 60.0
Dim newValues = myList.Where(Function(value, index) index Mod (1 / percent) < percent).ToList()

They are practically the same. But now let me explain how this works a bit now.

List<T> in .Net inherits from the IEnumerable<T> class which has the Where() overloaded extension method from the Linq library. Using linq in combination with a lambda expression Function(value, index) index Mod (1 / percent) < percent I was able to specify to the Where method exactly what values to give back. I specifically did this with some math involving the index of the item that was being iterated over internally. Suffice to say, the Where method loops over given list, and returns all values which meet the criteria given in that lambda expression. At the end I convert the IEnumeral<double> to a List<double> to work like the data you had before.

| improve this answer | |
  • 2
    \$\begingroup\$ Using LinQ is a great idea. I will check how it performs and then accept this answer. \$\endgroup\$ – Jens Apr 11 '14 at 15:20
  • 1
    \$\begingroup\$ This works flawlessly, great job! \$\endgroup\$ – Jens Apr 11 '14 at 15:26
  • \$\begingroup\$ The power of Linq! Glad it worked out for you. \$\endgroup\$ – BenVlodgi Apr 11 '14 at 16:29
  • \$\begingroup\$ This does not work correctly for all values as can be seen in fiddle here (with 7 of 36): dotnetfiddle.net/QBw3zU \$\endgroup\$ – Rickard Liljeberg Oct 29 '18 at 12:20

change all of your integer values to Float variables, for a start. since I assume that you want a Decimal representation for numbers in between two numbers.

if that is not the case and you actually want to select every other number, then you should use a for code block

I am still not exactly sure what you are trying to do, and this kind of sounds like it might be an off-topic question

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.