1

I have a small puzzle that's giving me headaches and should be quite enjoyable to solve.

I have a set of arrays

arrayX : array of real;
arrayY : array of real;

that represent a number of points x,y such that ( arrayX[0], arrayY[0] ) constitutes a point. Now, I want to sort these arrays with respect to X and I'm thinking the way must be to get a list of sorted indexes by arrayX and apply this to both arrays, and here my problem arises:

How to write a function that gives me the sorted indexes (ascending) of arrayX, preferably in an array of integers? ArrayX can hold duplicate values

2
  • 1
    Why not using a Array or RealPoint? type TRealPoint=Record x:Double; y:Double; End; aArray=Array of TRealPoint; Commented Sep 5, 2014 at 10:46
  • the actual problem has been simplified to explain what I've been trying to do and is actually a large number of arrays that are part of an even larger structure that would take a lot of work to rewrite the way you suggest hooray for legacy code that is too much hazzle to rewrite Commented Sep 5, 2014 at 10:53

2 Answers 2

9

I'm assuming that you already have sorting capability in your code, and am not going to attempt to explain how to sort here. The RTL provides TArray.Sort<T> for this purpose.

Instead of sorting the values, sort indices. Add a level of indirection.

  1. Create an array of integer, Indices, containing the indices 0, 1, ..., N-1.
  2. Sort this array of integers.
  3. When comparing two values L and R from the array of indices, instead of comparing L and R, compare X[L] and X[R].

So, to expand on the final point, a standard compare function when sorting integers looks like this:

function Compare(L, R: Integer): Integer;
begin
  if L<R then
    Result := -1
  else if L>R then
    Result := 1
  else
    Result := 0;
end;

But instead, you apply indirection at this point:

function Compare(L, R: Integer): Integer;
begin
  if X[L]<X[R] then
    Result := -1
  else if X[L]>X[R] then
    Result := 1
  else
    Result := 0;
end;

At the end of this process you have indices that tell you the order of the points. The ith point is:

X[Indices[i]], Y[Indices[i]]

This technique is known as indirect sorting.


The problem that you present does suggest though that you may not have defined your data structures correctly. Instead of two distinct arrays, one containing the X coordinates, and one containing the Y coordinates, it would seem more appropriate to store a single array of points:

type
  TPoint = record
    X: Real;
    Y: Real;
  end;

var
  Points: array of TPoint;

Now you can sort Points by ordering on the X values, but exchanging entire points. When you represent the data this way, there is no possibility for the coordinates to get jumbled up. And X coordinate can never get separated from its matching Y coordinate.

Sign up to request clarification or add additional context in comments.

5 Comments

Very useful answer. I will take this into account I agree that the design is flawed and that a record structure would serve me better. Alas, the structure is much larger than I'm letting on here and a lot of functionality depends on the current design (don't we all love projects we take over from previous employees?), making a rewrite infeasible.. Sometimes you just have to hold your nose and make the best of it
It should be no problem to get an array of real from array of TPoint: function arrayX( Points : TPoints ) : array of real; to not break your dependencies until you restructure the whole source
@user I understand, but I felt compelled to make the point for sake of completeness
+1 for indirect sorting. Indirect sorting has many advantages. First is the fact that you don't need to move your actual data. This can greatly improve performance when you are daling with large arrays of large objects. Second advantage which could come especially usefull in your situation is that becouse you haven't actually done any data movment you can go and create multiple indice arrays, one for each array of data and sort them based on data of that specific array. So you end up with your data indirectly sorted based on values of each array. It continues...
... I can already hear your boss saying: "Great job, you managed to sort all the data based on this array. Now I want you to do the same and implement the sorting of all data on that array. Also I want the ability to change between the two sorting implementations whenever I want." Using direct soring would mean that in order to satisfy your bous you would have to resort your data evry time the sorting approach is changed. But using indirect sorting and multiple indice arrays you will have all that data already sorted. It is like using of mutiple indexes in same database table.
1

I think I've figured out the way to do it: 1) make a temporary array that is a copy of the input 2) find the minimum value of the temporary array 3) store the index of the minimum value 4) set the minimum value to NaN in the temporary array 5) repeat 2-5 until the temporary array no longer has numbers

Function indexedSort( inputArray : array of real ) : array of integer;
var
  i,j : integer;
  n : integer;
  minVal : real;
  tempArray : TArrayOfReal;
begin
  n := length(inputArray);
  setLength(result,n);
  tempArray := copy(inputArray,0,n);
  for i:= 0 to n-1 do begin
    for j := 0 to n-1 do begin
      if not(IsNan(tempArray[j])) then begin //find any non-NaN value for minimum
        minVal := tempArray[j];
        break;
      end;
    end;
    for j:=0 to n-1 do begin  //find actual min val
      if not(isNan(tempArray[j])) then begin
        if tempArray[j] <= minVal then begin
          minVal := tempArray[j];
          result[i] := j;
        end;
      end;
    end;
    tempArray[result[i]] := NaN;
  end;
end;

1 Comment

This isn't a great solution. Not least because the sorting is inefficient, O(N**2). It also precludes sorting values that may contain NaN. And more generally, it could only be applicable to types that admit sentinels. A separate array of booleans to mark that the value had been processed would get around all of that. Even so, indirection search is cleaner.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.