11

Consider the following case:

list1 = {q, r, s};
list2 = {{a, 2}, {b, 3}, {c, 1, f}};
Inner[{#1, #2} &, list1, list2, List]
(* Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1, f}}} *)

However, if the last element of list2 has Length 2:

list1 = {q, r, s};
list2 = {{a, 2}, {b, 3}, {c, 1}};
Inner[{#1, #2} &, list1, list2, List]
(* Out[] := {{{q, a}, {r, b}, {s, c}}, {{q, 2}, {r, 3}, {s, 1}}} *)

I would have expected (and wanted) a result like:

(* Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}} *)

So,

  1. Why does this Inner operation change if the list2 entries are all of the same length?
  2. How can I generate an output like the expected result?

Edit I came up with a potential answer to question 2:

Partition[Riffle[list1, list2], 2]
Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}}

but this seems somehow too-complicated.

Eli Lansey
  • 7,499
  • 3
  • 36
  • 73

2 Answers2

10

Inner is a generalization of Dot, and Dot has specific behavior for vectors, matrices and tensors:

Mathematica graphics

{a, b, c} . {x[1, 2], x[3, 4], x[5, 6]}

{a, b, c} . {{1, 2}, {3, 4}, {5, 6}}
a x[1, 2] + b x[3, 4] + c x[5, 6]

{a + 3 b + 5 c, 2 a + 4 b + 6 c}

When Inner detects a complete tensor it changes behavior in a similar fashion.
Likewise the second and third arguments of Inner are order-dependent like the arguments of Dot.

Just as x was used in Dot above, one can use an arbitrary head to "shield" the tensor depth from Inner. Unevaluated is a nice choice in this case because it will be automatically stripped by &.

list1 = {q, r, s};
list2 = {{a, 2}, {b, 3}, {c, 1}};

Inner[{#1, #2} &, list1, Unevaluated /@ list2, List]
{{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}}

Another option would be:

Inner[{#, #2[[1]]} &, list1, "" /@ list2, List]

As already mentioned by J. M., for #2:

{list1, list2}\[Transpose]
{{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • +1. It's probably a good idea to use MapAt with Unevaluated since you only need one modification for it to stop being a full array – Rojo Aug 09 '12 at 00:29
  • @Rojo that could work but Unevaluated is not a general solution (for example with List in place of {#1, #2} & which does not evaluate) so I think the second method may be more appropriate, and doing that for only one element would complicate things. (thanks for the vote.) – Mr.Wizard Aug 09 '12 at 03:18
  • I see your point. Perhaps this as ugly as it is could be more general Inner[List, list1, #, List] &[ MapAt[Sequence, Unevaluated[#] &[list2], {1, 1}]]. More general is still not general. It wouldn't work with HoldComplete instead of the first List – Rojo Aug 09 '12 at 03:32
4

I came up with a better answer to the second part of my question:

Thread[{list1, list2}]
(* Out[] := {{q, {a, 2}}, {r, {b, 3}}, {s, {c, 1}}} *)

which works equally well if the entries in the lists are of different lengths.

Eli Lansey
  • 7,499
  • 3
  • 36
  • 73