7

As the title, if I have a list:

{"", "", "", "2$70", ""}

I will expect:

{"", "", "", "2$70", "2$70"}

If I have

{"", "", "", "3$71", "", "2$72", ""}

then:

{"", "", "", "3$71", "3$71", "2$72", "2$72"}

And

{"", "", "", "3$71", "","", "2$72", ""}

should give

{"", "", "", "3$71", "3$71", "", "2$72", "2$72"}

This is my try:

{"", "", "", "2$70", ""} /. {p : Except["", String], ""} :> {p, p}

But I don't know why it doesn't work. Poor ability of pattern match. Can anybody give some advice?

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
yode
  • 26,686
  • 4
  • 62
  • 167

6 Answers6

8

In place modification of a list works well here:

fc[list_] := Module[{out=list},
    With[{empty = Pick[Range[2,Length@list], Rest@list,""]},
        out[[empty]]=out[[empty-1]]
    ];
    out
]

A comparison with Mr Wizard's fn:

data = RandomChoice[{"","","","a","b"}, 10^5];
r1 = fn[data]; //AbsoluteTiming
r2 = fc[data]; //AbsoluteTiming
r1 === r2

{0.049858,Null}

{0.01092,Null}

True

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
7

As I presently interpret the question

(Now with refinements after considering Chris Degnen's simultaneous answer)

fn[list_] :=
  Partition[list, 2, 1, -1, ""] // Cases[{p_, ""} | {_, p_} :> p]

Test:

 {"", "x", "y", "", "z", ""} // fn
{"", "x", "y", "y", "z", "z"}

Patterns

Since you seem only to be interested in a pattern-matching solution here is my proposal to avoid the extremely slow use of ReplaceRepeated while still using pattern-matching as the core of the operation.

fn2[list_] :=
  list // ReplacePart @ 
   ReplaceList[list, {a___, p_, "", ___} :> (2 + Length@{a} -> p)]

Recursive replacement

I just realized that this is a perfect time to use a self-referential replacement:

fn3[list_] :=
  list /. {a___, p_, "", b___} :> {a, p, p, ##& @@ fn3@{b}}

Benchmark

All three methods are much faster than kglr's foo (note the log-log scale).

Now with Carl Woll's fc, the fastest method yet. ( but no patterns ;-) )

Needs["GeneralUtilities`"]

$RecursionLimit = 1*^5;

BenchmarkPlot[{foo, fn, fn2, fn3, fc},
  RandomChoice[{"", "", "", "a", "b"}, #] &
]

enter image description here

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
4

Update:

foo = # //. {a___, p : Except["", _String], Longest[b : "" ..],   c___} :>
   {a, p, p, ## & @@ ConstantArray["☺", Length[{b}] - 1], c} /. "☺" -> "" &;

{"", "", "", "3$71", "", "", "2$72", "", "", "", ""} // foo

{"", "", "", "3$71", "3$71", "", "2$72", "2$72", "", "", ""}

Previous post:

rule = {a___, p : Except["", _String], Longest["" ..],  b___} :> {a, p, p, b};

{"", "", "", "3$71", "", "", "2$72", "", "", "", ""} //. rule

{"", "", "", "3$71", "3$71", "2$72", "2$72"}

kglr
  • 394,356
  • 18
  • 477
  • 896
4

Borrowing kglr's pattern

x = {"", "", "1$71", "3$71", "", "", "2$72", "", "", "", ""};

Prepend[
 Map[Last, Partition[x, 2, 1] /. {p : Except[""], ""} :> {p, p}],
 First[x]]

{"", "", "1\$71", "3\$71", "3\$71", "", "2\$72", "2\$72", "", "", ""}

user might consider to use Apply rather than Map:

Last@@@(Partition[x, 2, 1] /. {p : Except[""], ""} :> {p, p})//Prepend[First@x]
Ali Hashmi
  • 8,950
  • 4
  • 22
  • 42
Chris Degnen
  • 30,927
  • 2
  • 54
  • 108
3

Using SequenceReplace (new in 11.3)

f = SequenceReplace[{a : Except[""], ""} :> Sequence[a, a]] @ #&;

f @ {"", "", "", "270", ""}

{"", "", "", "270", "270"}

f @ {"", "", "", "371", "", "272", ""}

{"", "", "", "371", "371", "272", "272"}

f @ {"", "", "", "371", "", "", "272", ""}

{"", "", "", "371", "371", "", "272", "272"}

Addendum

As Syed commented a better solution would be

f = SequenceReplace[{a_String, ""} :> Sequence[a, a]] @ #&;

f @ {"", "", "", "270", ""}

{"", "", "", "270", "270"}

eldo
  • 67,911
  • 5
  • 60
  • 168
  • This works, albeit it is making redundant substitutions by not checking for empty strings: SequenceReplace[list, {a_String, ""} :> Sequence[a, a]] where list is your input. – Syed Jan 12 '24 at 06:39
  • Thanks, Syed, I updated my answer – eldo Jan 12 '24 at 06:47
1

Using SequenceSplit:

sp[lst_] := SequenceSplit[lst, s : {Except[""], ""} :> s]

pos1 = Position[sp[l1], {a_String, _} /; DigitQ[a]];

Flatten@ReplacePart[sp[l1], Thread[pos1 -> Cases[sp[l1], {a_, _} :> {a, a}]]]

({"", "", "", "270", "270"})

pos2 = Position[sp[l2], {a_String, _} /; DigitQ[a]];

Flatten@ReplacePart[sp[l2], Thread[pos2 -> Cases[sp[l2], {a_, _} :> {a, a}]]]

({"", "", "", "371", "371", "272", "272"})

pos3 = Position[sp[l3], {a_String, _} /; DigitQ[a]];

Flatten@ReplacePart[sp[l3], Thread[pos3 -> Cases[sp[l3], {a_, _} :> {a, a}]]]

({"", "", "", "371", "371", "", "272", "272"})

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44