add pairs with a rule to a list
Join @@ Developer`PartitionMap[If[Length@# === 1, #, {#[[1]], {#[[2, 1]], #[[1, 2]]}}] &,
list1, 2, 1, 1, {}] == list2
True
Join @@ (If[Length@# === 1, #, {#[[1]], {#[[2, 1]], #[[1, 2]]}}] & /@
Partition[list1, 2, 1, 1, {}]) == list2
True
Steps:
Partition list
into parts of length up to 2 with offset 1 (take 2 elements move 1 step):
l1 = Partition[list1, 2, 1, 1, {}]
{{{-2.25, 24}, {-2.24, 0}},
{{-2.24, 0}, {-2.23, 0}},
{{-2.23, 0}, {-2.22, 10}},
{{-2.22, 10}, {-2.21, 32}},
{{-2.21, 32}, {-2.2, 50}},
{{-2.2, 50}}}
The function
func = If[Length@# === 1, #, {#[[1]], {#[[2, 1]], #[[1, 2]]}}] &;
leaves objects with Length
1 untouched, and, for two-element arguments modifies the second element to make its last entry equal to that of the first.
Applied to the first element of l1
it gives
func @ l1[[1]]
{{-2.25, 24}, {-2.24, 24}}
Mapped to each element of l1
it gives
func /@ l1
{{{-2.25, 24}, {-2.24, 24}},
{{-2.24, 0}, {-2.23, 0}},
{{-2.23, 0}, {-2.22, 0}},
{{-2.22, 10}, {-2.21, 10}},
{{-2.21, 32}, {-2.2, 32}},
{{-2.2, 50}}}
Flattening this gives list2
.
Developer`PartititionMap[func, list, other arguments]
is equivalent to func /@ Partition[list, other arguments]
, that is, it applies the function to each element of the partition.
You can also use ReplaceAll
list = {{-2.25, 24}, {-2.24, 0}, {-2.23, 0}, {-2.22, 10}, {-2.21, 32}, {-2.20, 50}};
Append[Partition[list, 2, 1] /. {p : {_, a_}, {b_, _}} :>
Sequence[p, {b, a}], Last@list]
{{-2.25, 24}, {-2.24, 24},
{-2.24, 0}, {-2.23, 0},
{-2.23, 0}, {-2.22,0},
{-2.22, 10}, {-2.21, 10},
{-2.21, 32}, {-2.2, 32},
{-2.2, 50}}
Or
Most@Catenate@MapThread[{#1, {First@#2, Last@#1}} &, {list, RotateLeft@list}]
J. M.'s method is much faster than others provided, and deserves its own answer.
The champion:
fastJM[a_] := Riffle[a, {Rest[#1], Most[#2]}\[Transpose] & @@ (a\[Transpose])]
The contenders:
kglr[a_] :=
Join @@ Developer`PartitionMap[
If[Length@# === 1, #, {#[[1]], {#[[2, 1]], #[[1, 2]]}}] &, a, 2, 1, 1, {}]
eldo1[list_] :=
Append[Partition[list, 2, 1] /. {p : {_, a_}, {b_, _}} :>
Sequence[p, {b, a}], Last@list];
eldo2[list_] :=
Most@Catenate@MapThread[{#1, {First@#2, Last@#1}} &, {list, RotateLeft@list}]
My own contribution, a variation of J.M.'s Riffle
code:
JMmod[a_] := Riffle[a, {a[[2 ;;, 1]], a[[;; -2, 2]]}\[Transpose]]
Benchmark
With a packed array of reals:
Needs["GeneralUtilities`"]
BenchmarkPlot[{fastJM, kglr, eldo1, eldo2, JMmod}, RandomReal[9, {#, 2}] &, 10]
With unpackable Strings:
BenchmarkPlot[{fastJM, kglr, eldo1, eldo2, JMmod},
RandomChoice[Alphabet[], {#, 2}] &, 10]
It appears that my variation manages a slight edge on the original in the case of unpackable data, but it falls behind on a packed array. None of the other methods come close in either test.