 How to avoid implemented the same function twice

I needed to call this function takeNsmallest on a List and also on a MapView. So I duplicated the function and only changed the type declarations.
Is there a better way in this case? Should I write the function once and just use cleverer declarations?

def takeNsmallest[T](n: Int, data: List[T], measure: T => Double): List[T] = {
if (n == 0 || data.isEmpty)
List()
else {
data
.foldLeft(MaxSizeList[T](n, measure, List[T]())) { (acc, next) => MaxSizeList(n, measure, next :: acc.items) }
.items
}.sortBy(measure)
}

def takeNsmallest[A,B](n: Int, data: MapView[A,B], measure: ((A,B)) => Double): List[(A,B)] = {
if (n == 0 || data.isEmpty)
List()
else {
data
.foldLeft(MaxSizeList[(A,B)](n, measure, List[(A,B)]())) { (acc, next) => MaxSizeList(n, measure, next :: acc.items) }
.items
}.sortBy(measure)
}

I think this should work:

def takeNsmallest[T](n: Int, data: IterableOnce[T], measure: T => Double): List[T] =
if (n == 0)
List.empty[T]
else
data
.iterator
.foldLeft(MaxSizeList[T](n, measure, List.empty[T])) {
(acc, next) => MaxSizeList(n, measure, next :: acc.items)
}.items
.sortBy(measure)

ahhh, so the same code doesn’t work. we have to add the .iterator call? Interesting.

Yeah, that is because IterableOnce doesn’t provide any methods at all.
Rather, it just provides a way to get an Iterator

I think you could use Iterable instead of IterableOnce.

Why?
That would just allow fewer data types without any advantage to the code.

No reason other than that the code would work without .iterator.

That’s an interesting question, whether a “native” foldLeft is more desirable.

List has both foldLeft and iterator that use tail, so maybe the differential cost is only an indirection. Maps resort to IterableOnceOps.

Arrays and LazyList get dedicated foldLeft as well.

Doc says minimal IterableOnce is to support parallel collections, which would not want to inherit IterableOnceOps.

The method could take IterableOnceOps, but probably no one does that.