# Finding the index of an element to be inserted in a sorted list

I have a sorted list with no duplicates:

L = {1, 2, 5, 7, 8}


Given an integer n, I want to find the index of n if it is to be inserted into L.

I have been using

Length[Select[L, # <= n &]]

But that is incredibly unoptimal as I should be able to do this in $$\log n$$ by bisecting the set. How should I approach this?

ClearAll[pos0, pos1, pos2, pos3]
pos0[l_, n_] := Total@UnitStep[n - l]
pos1[l_, n_] := LengthWhile[l, # < n &]
pos2[l_, n_] := Ordering[Ordering[Join[{n}, l]]][[1]]
pos3[l_, n_] := If[n <= Length[l]/3, pos1[l, n], pos2[l, n]]

L = {1, 2, 5, 7, 8};
{pos0[L,3], pos1[L, 3], pos2[L, 3], pos3[L, 3]}


{2, 2, 2, 2}

You could use a variation of my LeftNeighbor function, although it returns the left neighbor strictly less than the number instead of less equal as in your solution:

LeftNeighbor[s_]:=LeftNeighborFunction[s,Nearest[s->"Index"]]
LeftNeighbor[s_,list_List]:=LeftNeighbor[s][list]
LeftNeighbor[s_, elem_]:=First@LeftNeighbor[s][{elem}]

LeftNeighborFunction[s_,nf_][list_List]:=With[{n=nf[list][[All,1]]},n-UnitStep[s[[n]]-list]]
LeftNeighborFunction[s_,nf_][elem_]:=First @ LeftNeighborFunction[s,nf][{elem}]

MakeBoxes[i:LeftNeighborFunction[s_,nf_], StandardForm] ^:= Module[
{
len=Length[s],
g = FirstCase[ToBoxes[nf], _GraphicsBox, GraphicsBox[Point[{0,0}]],Infinity]
},
BoxFormArrangeSummaryBox[
LeftNeighborFunction,
i,
RawBoxes@g,
{
BoxFormMakeSummaryItem[{"Data points: ",Length[s]},StandardForm],
BoxFormMakeSummaryItem[{"Range: ",MinMax[s]},StandardForm]
},
{},
StandardForm,
"Interpretable"->True
]
]


Then:

L={1,2,5,7,8};
lnf = LeftNeighbor[L];

lnf[5]
lnf[Range[0, 9]]


2

{0, 0, 1, 2, 2, 2, 3, 3, 4, 5}

It should be possible to modify the code to return the left neighbor that is less equal than the requested number if desired.

A remark on performance

The construction of the LeftNeighborFunction can be slow, but if you want to find the index of many numbers into the same list L, then you will only have to construct the LeftNeighborFunction once. Here is a comparison with a large list:

L = Sort @ RandomReal[100, 10^6];


The construction of the LeftNeightbor is a bit slow:

lnf = LeftNeighbor[L]; //RepeatedTiming


{0.052, Null}

But finding the index of many numbers is very fast:

sample = RandomReal[100, 10^3];
r1 = lnf[sample]; //RepeatedTiming


{0.00020, Null}

Compare this with one of kglr's solution:

r2 = pos0[L, #]& /@ sample; //RepeatedTiming


{3.05, Null}

Check :

r1 === r2


True

It seems to me that the issue of how to handle inappropriate inputs is as important finding the position of an integer that is in the list being considered. Therefore, I would write something like this:

positionOfN[data : {__Integer}, n_Integer] :=
First[First[Position[data, n], Return[$Failed]]] positionOfN[_, _] =$Failed;


### Testing

data = {1, 2, 5, 7, 8};
positionOfN[data, 5]


3

positionOfN[data, -5]


$Failed positionOfN[data, 5.]  $Failed

positionOfN[{"foo", "bar"}, 5]