14
$\begingroup$

I have a huge list with huge sublists of the form

list={{a,b,c,d},{e,f,g,h},{i,j,k,l}}

I am looking for a way to manipulate these sublists based on the positions of the elements. Something like

list/.{j_,k_,l_,m_}->{j-1,k,l,m}

but without having to write the whole pattern. Is there any way to specify such manipulation based on the position of the element? Something like

list/.#[[1]] & -> #[[1]] - 1 &

that would work?

$\endgroup$
9
  • 1
    $\begingroup$ Do you want (for example) to subtract one from the first element in each sublist? $\endgroup$ Commented Feb 17, 2014 at 18:56
  • $\begingroup$ Are the sublists all of the same length? $\endgroup$
    – Szabolcs
    Commented Feb 17, 2014 at 18:58
  • $\begingroup$ f[l_List] := {First@l - 1, Sequence @@ Rest@l}; f /@ list ? $\endgroup$ Commented Feb 17, 2014 at 18:59
  • 1
    $\begingroup$ MapAt[# - 1 &, list, {All, 1}] or {#1 - 1, ##2} & @@@ list. $\endgroup$
    – Artes
    Commented Feb 17, 2014 at 19:01
  • 2
    $\begingroup$ Could you guys please post these as answers? Although they are somewhat redundant (given that they all should solve the problem), it is nice to see for others to see the various possibilities to solve this $\endgroup$
    – Sos
    Commented Feb 17, 2014 at 19:08

12 Answers 12

16
$\begingroup$

You could use: MapAt:

MapAt[ # - 1 &, list, {All, 1}]
{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

or Apply at the first level (shorthand @@@) (see also SlotSequence, shorthand ##n):

{#1 - 1, ##2}& @@@ list
$\endgroup$
1
  • $\begingroup$ Your first answer, together with @andre's answer, seems to be the most robust since I can specify the position to to the subtraction, without having to do it only in the 1st position. $\endgroup$
    – Sos
    Commented Feb 18, 2014 at 16:30
15
$\begingroup$

This is quite neat:

list[[;; , 1]]--
$\endgroup$
2
  • 2
    $\begingroup$ It yields {a, e, i} for the list in the OP so one has to evaluate list twice to get the result of list /. {j_, k_, l_, m_} -> {j - 1, k, l, m}, since it changes list I find it's rather a drawback. $\endgroup$
    – Artes
    Commented Feb 18, 2014 at 10:30
  • 1
    $\begingroup$ @Artes Indeed. Well, after this list is updated, the method you want to use depends of what OP needs at the end. $\endgroup$
    – Kuba
    Commented Feb 18, 2014 at 10:33
8
$\begingroup$

If you write

list[[All, 1]] = list[[All, 1]] - 1

list will be updated with the new values of the first element of each sublist.

$\endgroup$
0
7
$\begingroup$

Maybe

ReplacePart[list, (x : {_, 1}) :> (Extract[list, x] - 1)]  

?

The positions of the elements are specified by the pattern on the indices : {_,1}

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

$\endgroup$
0
6
$\begingroup$

You might be able to use this rule-based method

Replace[list, {f_, r___} -> {f - 1, r}, {1}]
{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

or something similar. This will work for any sublist length.

list2 = {{a, b, c, d}, {e, f, g}, {h}};
Replace[list2, {f_, r___} -> {f - 1, r}, {1}]
{{-1 + a, b, c, d}, {-1 + e, f, g}, {-1 + h}}

More generally

munge[func_, data_] := Replace[data, {f_, r___} :> {func@f, r}, {1}]
munge[# - 1 &, list]
{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}
$\endgroup$
2
  • $\begingroup$ I had been trying to play with replacement rules but to no avail, but now I see why. Btw, would there be a way to specify the position in which the replacement would have to be made? e.g. if I wanted to apply the rule to each element in position 37 of sublists with 100 elements? $\endgroup$
    – Sos
    Commented Feb 18, 2014 at 16:26
  • $\begingroup$ Nevermind, I guess that would have to be something similar to what @andre answered $\endgroup$
    – Sos
    Commented Feb 18, 2014 at 16:28
5
$\begingroup$

That's a prime example to use Threaded which was introduced in v13.1

list = {{a, b, c, d}, {e, f, g, h}, {i, j, k, l}};
list + Threaded[{-1, 0, 0, 0}]

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

$\endgroup$
4
  • 2
    $\begingroup$ +1 - That's probably the nicest solution $\endgroup$
    – eldo
    Commented Dec 20, 2023 at 23:50
  • $\begingroup$ @eldo Thanks a lot :-) $\endgroup$
    – bmf
    Commented Dec 20, 2023 at 23:53
  • 1
    $\begingroup$ (+1 ) I agree with @eldo, it seems like the nicest solution to me. :-) $\endgroup$ Commented Dec 20, 2023 at 23:56
  • $\begingroup$ @E.Chan-López thanks a lot mate :-) $\endgroup$
    – bmf
    Commented Dec 21, 2023 at 0:02
3
$\begingroup$
list = {{a, b, c, d}, {e, f, g, h}, {i, j, k, l}};

We can also use ReplaceAt (new in 13.1)

ReplaceAt[x_ :> x - 1, {All, 1}] @ list

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

One of its advantages is that we can easily add conditions:

ReplaceAt[x_ /; x =!= e :> x - 1, {All, 1}] @ list

{{-1 + a, b, c, d}, {e, f, g, h}, {-1 + i, j, k, l}}

To update list inline we can employ ApplyTo (new in 12.2)

list //= ReplaceAt[x_ :> x - 1, {All, 1}];

list

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

$\endgroup$
3
$\begingroup$

Using Cases:

list = {{a, b, c, d}, {e, f, g, h}, {i, j, k, l}};

Cases[list, x_ :> {-1 + First@x, Sequence @@ Rest@x}]

(*{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}*)

Edit Or as @eldo suggests, an equivalent way of doing it using Splice instead of Sequence:

Cases[list, x_ :> {-1 + First@x, Splice@Rest@x}] (*Thanks, eldo!*)

(*{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}*)
$\endgroup$
1
  • 2
    $\begingroup$ Very nice, +1, you could also use Splice@ instead of Sequence@@ $\endgroup$
    – eldo
    Commented Dec 20, 2023 at 17:20
3
$\begingroup$
list + ConstantArray[{-1,0,0,0},3]

(* {{-1+a,b,c,d},{-1+e,f,g,h},{-1+i,j,k,l}} *)
$\endgroup$
1
  • 1
    $\begingroup$ That's, also, very slick! $\endgroup$
    – bmf
    Commented Dec 21, 2023 at 9:53
2
$\begingroup$

Using SubetMap:

Clear["Global`*"];
list = {{a, b, c, d}, {e, f, g, h}, {i, j, k, l}};
SubsetMap[# - 1 &, list, {All, 1}] // Column

Result:

enter image description here

$\endgroup$
2
$\begingroup$
list = {{a, b, c, d}, {e, f, g, h}, {i, j, k, l}};

1.

A shorter Cases - variant

Cases[{a_, b__} :> {a - 1, b}] @ list

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

2.

Using Query

Query[All, {#[[1]] - 1 &, Splice @* Rest}] @ list

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

$\endgroup$
2
  • $\begingroup$ (+1) Also Query[All, {1 -> (# - 1 &)}]@list (after Sjoerd Smit) $\endgroup$
    – user1066
    Commented Jun 27 at 19:48
  • $\begingroup$ Thanks, 1066, your suggestion is nicer and more appropriate in this case. $\endgroup$
    – eldo
    Commented Jun 27 at 22:14
2
$\begingroup$
list = {{a, b, c, d}, {e, f, g, h}, {i, j, k, l}};

A variant of @andre314's answer with ReplacePart using Part instead of Extract is the following:

ReplacePart[#, {m_, 1} :> #[[m, 1]] - 1] &@list

{{-1 + a, b, c, d}, {-1 + e, f, g, h}, {-1 + i, j, k, l}}

$\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.