3
$\begingroup$

I essentially have two questions, both are related.

1) How do I change a value within a list of lists (I provided a example problem)?

2) Why does my code not work?

I have a list which was created from the Tally function on a text file. The result is shown below...

tally = {{"tree", 5}, {"State", 6}, {"swimming", 3}, {"began", 
2}, {"season", 8}, {"Force", 2}, {"Sooners", 10}, {"three", 
4}, {"second", 14}, {"medley", 6}, {"relay", 17}, {"junior", 
5}, {"sophomore", 3}, {"freshman", 5}, {"coach", 2}, {"Pratt", 
2}, {"think", 2}, {"close", 2}, {"thought", 2}, {"events", 
8}, {"finished", 9}, {"individual", 4}, {"collected", 
4}, {"honors", 4}, {"along", 2}, {"third", 13}, {"showing", 
4}, {"swept", 2}, {"first", 5}, {"Junior", 2}, {"star", 
22}, {"freestyle", 4}, {"runner", 2}, {"Sooner", 2}, {"scored", 
2}, {"respectively", 2}, {"finishes", 4}, {"Sophomore", 
2}, {"opening", 3}, {"finish", 2}, {"lifetime", 2}, {"times", 
2}, {"posted", 3}, {"marks", 2}, {"Championships", 
3}, {"conference", 2}, {"recorded", 2}, {"vital", 2}, {"relays", 
4}, {"school", 3}, {"records", 2}, {"earned", 3}, {"fourth", 
3}, {"campaign", 2}, {"against", 5}, {"Northern", 6}, {"Colorado",
 5}, {"helped", 2}, {"Arizona", 2}, {"Idaho", 2}, {"named", 
2}, {"Academic", 2}, {"backstroke", 2}, {"fifth", 3}, {"regular", 
2}, {"winning", 2}, {"victorious", 2}, {"sixth", 2}, {"RioRancho",
 4}, {"School", 2}};

I want to be able to find the string "star" within the list and change the associated number with that string to 1000. I tried using the following code:

tally /. _?Last@(First@# == "star" &) -> 1000

It does not work the way I want. The number value associated with "star" remains unchanged. I know there is a way to do this. I just can't see it...

Thank you for any assistance!

$\endgroup$
3
  • 7
    $\begingroup$ Why not just tally /. {"star", _} -> {"star", 1000} ? $\endgroup$ Commented Jan 24, 2013 at 20:47
  • 2
    $\begingroup$ The First part of your pattern works alright, but you can't take the Last of that. It doesn't mean that the last part of your matching is replaced by 1000. $\endgroup$ Commented Jan 24, 2013 at 21:22
  • 2
    $\begingroup$ Simon gave you the solution. Why your version doesn't work is twofold: 1. ? must be followed by a function. First@# == "star" & is a function but (Last@(First@# == "star" &)) is not 2. you are replacing the whole thing that _ ? ... matches not just the second part of that subexpression. $\endgroup$
    – Szabolcs
    Commented Jan 24, 2013 at 21:31

4 Answers 4

5
$\begingroup$

Here are another 3 options:

If[#[[1]] == "star", {"star", 1000}, #] & /@ tally

But this is a terrible option, I just wrote for pedagogical reasons. Let's see another one:

Replace[tallyBig, {"star", _} -> {"star", 1000}, 1];

This is better, due too level specification, I believe MMA does't waste time looking where it's not necessary. Now the quickest that I get was:

ReplacePart[tallyBig,Position[tallyBig[[All,1]],"star",1]->{"star",1000}]

Let's compare performance:

tallyBig = RandomChoice[tally, 1*^6]

(*Murta1*)   If[#[[1]]=="star",{"star",1000},#]&/@tallyBig;//AbsoluteTiming
(*Szabolcs*) tallyBig/.{"star",_}->{"star",1000};//AbsoluteTiming
(*Murta2*)   Replace[tallyBig,{"star",_}->{"star",1000},1];//AbsoluteTiming
(*Stefan*)   Scan[(tallyBig[[#,2]]=1000)&,Position[tallyBig,"star",{2}][[All,1]]];//AbsoluteTiming
(*Murta3*)   ReplacePart[tallyBig,Position[tallyBig[[All,1]],"star",1]->{"star",1000}];//AbsoluteTiming

{1.294130, Null}
{0.843827, Null}
{0.141805, Null}
{0.133081, Null}
{0.092666, Null}

Now Murta3 is the best option and Murta2 and Stefan gets almost the same result.

$\endgroup$
4
  • $\begingroup$ +1 for options, timings, and a fast method. BTW, tallyBig = RandomChoice[tally, 1*^5]; might be a better test list. $\endgroup$
    – Mr.Wizard
    Commented Feb 15, 2013 at 1:17
  • $\begingroup$ Tks! I agree, tallyBig updated. :) $\endgroup$
    – Murta
    Commented Feb 15, 2013 at 1:27
  • $\begingroup$ Thank you very much for a further analysis of my question. This will be good for a much larger data evaluation and analysis, which is coming soon for me! $\endgroup$
    – Joseph
    Commented Feb 15, 2013 at 19:45
  • $\begingroup$ I'm glad to help. Thank you for choosing my answer. :) $\endgroup$
    – Murta
    Commented Feb 15, 2013 at 21:08
3
$\begingroup$

You could also try to get the positions of your string

pos = Position[tally, "star", {2}][[All, 1]]

and then you could use these positions to replace the value.

Scan[(tally[[#, 2]] = 1000) &, pos]

The variable tally is directly modified.

$\endgroup$
1
  • $\begingroup$ Nice Scan and Position combination. +1 $\endgroup$
    – Murta
    Commented Feb 15, 2013 at 0:41
1
$\begingroup$

You can use the replacement

tally /. {"star", _} -> {"star", 1000}

Why your version doesn't work is twofold:

  1. ? must be followed by a function. First@# == "star" &is a function but (Last@(First@# == "star" &)) is not

  2. you are replacing the whole thing that _ ? ... matches not just the second part of that subexpression.

$\endgroup$
0
$\begingroup$

This answer shows how to define functions that do what is requested.

The first two solutions generate a brand new list. They do not modify the list in place. The third solution modifies the list in place.

Solution 1

replaceViaCopy[lis_, key_, rep_] := 
  ReplacePart[lis, {Position[lis, {key, _}][[1, 1]], 2} -> rep];

test = {{"tree", 5}, {"State", 3000}, {"swimming", 
   3}, {"began", 2}, {"season", 8}, {"Force", 2}, {"Sooners", 
   10}}

replaceViaCopy[test, "State", 6000]

returns

{{"tree", 5}, {"State", 6000}, {"swimming", 3}, {"began", 
  2}, {"season", 8}, {"Force", 2}, {"Sooners", 10}}

Solution 2

This definition is clearer and shorter.

replaceViaCopy2[lis_, key_, rep_] := lis /. {key, _} -> {key, rep}

Solution 3

To change the list without creating a new copy

SetAttributes[replaceInPlace, HoldFirst];

replaceInPlace[lis_, key_, rep_] := 
         (lis[[Position[lis, {key, _}][[1, 1]], 2]] = rep;)

Then

replaceViaCopy[test, "State", 6000]

will not return anything, but will modify test.

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