The modeling of the data is of questionable quality: why exactly do you want to represent everything as String arrays? But well, here you go:
val order: Array[Array[String]] = Array(
Array("o2","ad1"),
Array("o1","ad2"),
Array("o7","ad5")
)
val line: Array[Array[String]] = Array(
Array("l1","o1","x"),
Array("l2","o2","y"),
Array("l3","o13","z")
)
val orderAsMap = order.map{x => (x(0), x(1))}.toMap
val leftOuterJoin = for (x <- line) yield (x :+ orderAsMap.getOrElse(x(1), "<NULL>"))
// print to see the result
for (row <- leftOuterJoin) println(row.mkString(", "))
// output:
//
// l1, o1, x, ad2
// l2, o2, y, ad1
// l3, o13, z, <NULL>
The intermediate toMap is necessary so that you don't have to traverse the entire array every time. The actual joining is done using the :+ operator.
Consider using an appropriate library if you have to deal with such questions regularly.
EDIT: Thanks @igorpcholkin for reminding me that "left join" is synonymous to "left OUTER join" in the usual nomenclature. The first version was rather something like an inner join. I'll leave it here just in case you want an inner join after all:
val order: Array[Array[String]] = Array(Array("o2","ad1"), Array("o1","ad2"))
val line: Array[Array[String]] = Array(
Array("l1","o1","x"),
Array("l2","o2","y"),
Array("l3","o13","z")
)
val orderAsMap = order.map{x => (x(0), x(1))}.toMap
val lj = for {
x <- line
if orderAsMap.contains(x(1))
} yield (x :+ orderAsMap(x(1)))
// print to see the result
for (row <- lj) println(row.mkString(", "))
// output:
//
// l1, o1, x, ad2
// l2, o2, y, ad1
orderhas no sub-array with the target string?