8
$\begingroup$

I have the following list of lists:

l = {{5,6,0,8,5,9}, {8,4,4,3,5,8}, {9,4,3,2,5,7}}

now if there is a 0 in one of the lists Mathematica should replace the last element in the list with 0 like this:

l1 = {{5,6,0,8,5,0}, {8,4,4,3,5,8}, {9,4,3,2,5,7}}

the l1 should be my output. I have no idea how is can do this, I hope someone can help.

$\endgroup$
6
  • 7
    $\begingroup$ l /. x_ /; MemberQ[x, 0] :> ReplacePart[x, -1 -> 0] $\endgroup$
    – Jason B.
    Commented Jul 28, 2016 at 18:02
  • 3
    $\begingroup$ @JasonB Dang it! I was 15 seconds too late. -- Here's an alternative: l1 = l /. a: {___, 0, ___ } :> ReplacePart[a, -1 -> 0] $\endgroup$ Commented Jul 28, 2016 at 18:03
  • 2
    $\begingroup$ ReplacePart is not necessary l1 /. {a___, 0, b___, _} -> {a, 0, b, 0}. I see no reason for RuleDelayed either? $\endgroup$
    – Coolwater
    Commented Jul 28, 2016 at 18:25
  • 5
    $\begingroup$ @Coolwater RuleDelayed is standard if you are using variables in both sides of Rule; it prevents naming conflicts, etc. For instance, a = 3; {1, 2, 3} /. a_Integer -> a does not work. One could clear variables prior to running ReplaceAll, but writing Clear every time there is /. is too inconvenient! $\endgroup$ Commented Jul 28, 2016 at 19:00
  • $\begingroup$ @Coolwater Nice method, but heed JHM's warning. $\endgroup$
    – Mr.Wizard
    Commented Jul 28, 2016 at 19:03

8 Answers 8

16
$\begingroup$
l[[All, -1]] = l[[All, -1]] Times @@@ Unitize@l ; l

or, better (thanks: @Mr.Wizard):

l[[All, -1]] *= Times @@@ Unitize@l ; l

{{5, 6, 0, 8, 5, 0}, {8, 4, 4, 3, 5, 8}, {9, 4, 3, 2, 5, 7}}

Update: Per @JasonB's comment, if you want to create a new list without changing l, you can use

Module[{l2 = #}, l2[[All, -1]] *= Times @@@ Unitize@l2 ; l2] &@l

or

ReplacePart[l, { i_, -1} :> Times @@ Unitize@l[[i]]]

to get

{{5, 6, 0, 8, 5, 0}, {8, 4, 4, 3, 5, 8}, {9, 4, 3, 2, 5, 7}}

$\endgroup$
4
  • 1
    $\begingroup$ Using *= is shorter: l[[All, -1]] *= Times @@@ Unitize@l; l $\endgroup$
    – Mr.Wizard
    Commented Jul 28, 2016 at 18:57
  • $\begingroup$ @Mr.W, yes, thank you! $\endgroup$
    – kglr
    Commented Jul 28, 2016 at 18:58
  • $\begingroup$ Nice! If you want to do this without changing the definition of the original list, ReplacePart[#, -1 -> Times @@ Unitize@#] & /@ l $\endgroup$
    – Jason B.
    Commented Jul 28, 2016 at 19:40
  • $\begingroup$ This one's elegant (+1), but (possibly) trails slightly behind ciao's method since IIRC, MMA doesn't have an efficient built-in for the product of a list of numbers in the same manner that it does for the sum (Total). $\endgroup$
    – LLlAMnYP
    Commented Jul 29, 2016 at 7:36
13
$\begingroup$

S/B quick (assuming non-negative list members as in OP):

Module[{ll = #}, ll[[Pick[Range@Length@ll, Min /@ ll, 0], -1]] = 0; ll] &

If negative members allowed, small performance impact:

Module[{ll = #}, ll[[Pick[Range@Length@ll, Min /@ Abs[ll], 0], -1]] = 0; ll] &
$\endgroup$
3
  • 2
    $\begingroup$ You always have a method that is at least three orders of magnitude faster than what I think of, and usually completely inscrutable to me lol. Nice $\endgroup$
    – Jason B.
    Commented Jul 28, 2016 at 20:44
  • 2
    $\begingroup$ @JasonB: Habit, from days of limited memory/limited CPU assembly programming. Downside for me: I'll revisit old quick-n-dirty, not commented code of mine and do a "WTF is this doing and how???"... $\endgroup$
    – ciao
    Commented Jul 28, 2016 at 20:49
  • 1
    $\begingroup$ Count on ciao to come up with something completely illegible, but be sure, it'll never unpack arrays or copy them with O(n) complexity. +1 $\endgroup$
    – LLlAMnYP
    Commented Jul 29, 2016 at 7:33
9
$\begingroup$

kglr's answer is really clever, but I'll just post this as well.

l /. x_ /; MemberQ[x, 0] :> ReplacePart[x, -1 -> 0]

or a slightly shorter variant using patterns instead of of MemberQ, from JHM.

l /. a : {___, 0, ___} :> ReplacePart[a, -1 -> 0]

Shorter still, you use patterns instead of ReplacePart, from Coolwater

l /. {a___, 0, b___, _} :> {a, 0, b, 0}
$\endgroup$
2
  • 1
    $\begingroup$ a variant I like: l /. a:{b___, _} /; MemberQ[a, 0] :> {b, 0}. $\endgroup$
    – rcollyer
    Commented Jul 29, 2016 at 1:28
  • $\begingroup$ @JasonB I like your ReplaceAll answers because one can learn more about the essence of programming in Mathematica, i.e. rules and patterns and in my opinion they add clarity and code maintainability. In the spirit of rules transformation, kglr and rcollyer answers work fine for me. $\endgroup$ Commented Jul 29, 2016 at 8:20
5
$\begingroup$

A bit differently than in the answers so far. Mapping on sublists:

If[MemberQ[#, 0] && Last[#] =!= 0, Most[#]~Append~0, #] & /@ l

Pattern matching:

l /. {pre__, _} /; MemberQ[{pre}, 0] :> {pre, 0}
$\endgroup$
2
  • 1
    $\begingroup$ I'm not sure what saves more time - cutting on the unnecessary Append if Last[#] === 0 or dropping the Last[#] =!= 0 test. $\endgroup$
    – LLlAMnYP
    Commented Jul 29, 2016 at 8:17
  • $\begingroup$ @LLlAMnYP It doesn't save space but I think the additional test saves time. It's rarely used and when it's, it surely has to beat construction. $\endgroup$
    – BoLe
    Commented Jul 29, 2016 at 9:21
5
$\begingroup$

Just an alternative

MapAt[0 &, l, {#, -1} & @@@ Position[l, 0, {2}]]

or

ReplacePart[l, {#, -1} -> 0 & @@@ Position[l, 0, {2}]]
$\endgroup$
3
$\begingroup$
list =
  {{5, 6, 0, 8, 5, 9},
   {8, 4, 4, 3, 5, 8},
   {9, 4, 3, 2, 5, 7},
   {1, 2, 3, 0, 0, 3}};

Using MapIf by Taliesin Beynon

ConditionalMap = ResourceFunction["MapIf"];

ConditionalMap[MapAt[0 &, -1], MemberQ[0]] @ list

{{5, 6, 0, 8, 5, 0},
{8, 4, 4, 3, 5, 8},
{9, 4, 3, 2, 5, 7},
{1, 2, 3, 0, 0, 0}}

$\endgroup$
3
$\begingroup$

Grabbing the @eldo's list:

list =
  {{5, 6, 0, 8, 5, 9},
   {8, 4, 4, 3, 5, 8},
   {9, 4, 3, 2, 5, 7},
   {1, 2, 3, 0, 0, 3}};

Using Replace at level 1:

Replace[list, x_ /; ! FreeQ[x, 0] :> Append[0]@*Most@x, 1]

{{5, 6, 0, 8, 5, 0}, {8, 4, 4, 3, 5, 8}, {9, 4, 3, 2, 5, 7}, {1, 2, 3, 0, 0, 0}}

Or using Cases and If:

Cases[list, x_ :> If[Times @@ x == 0, Append[0]@*Most@x, x]]

{{5, 6, 0, 8, 5, 0}, {8, 4, 4, 3, 5, 8}, {9, 4, 3, 2, 5, 7}, {1, 2, 3, 0, 0, 0}}

$\endgroup$
3
$\begingroup$

If all members list numbers:

l = {{5, 6, 0, 8, 5, 9}, {8, 4, 4, 3, 5, 8}, {9, 4, 3, 2, 5, 7}};
f[n_] := 
 Last@FoldWhile[Flatten[{#1, #2}] &, l[[n, {1, 2}]], 
   l[[n, 2 ;;]], #[[-1]] #[[-2]] != 0 &]
MapIndexed[If[f[#2[[1]]] == 0, Most[#1]~Join~{0}, #1] &, l]

{{5, 6, 0, 8, 5, 0}, {8, 4, 4, 3, 5, 8}, {9, 4, 3, 2, 5, 7}}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.