# Who's Who in the Name-Change Game?

Very recently, I changed my SE username because I made this account some years ago and wanted to get back into asking questions. However, I’m still planning to post questions and potentially earn votes. We can imagine a site that has some issues dealing with name changes. The problem: given a list of names, reputation changes, and name changes, can you create a leaderboard of reputation? Assume everyone's reputation starts at 0.

Input comes in any reasonable format, and has two forms: a name change <String, String> and a rep change <String, Number>.

What does any reasonable format mean? When I write questions, it usually means the input can be rearranged or repackaged to work with the way your language takes in input (do you want a list of pairs? two lists? a string? that’s all fine) as long as it doesn’t add information or otherwise offload some of the computational work onto the human.

Name changes change a person’s username to any username not currently in use. For example, if A changes name to D, and B changes name to C, D (previously known as A) could still change their name to A or to B, even though these names have been used before. However, at any given moment the set of names are unique.

Rep changes represent something like getting up/down votes on a post, receiving a bounty, etc. All rep changes are guaranteed to be integers. They might come in the form <“Bob”, 5> or <“Alice”, -53000>. At the end, you must output all usernames that are currently in existence, and their rep counts, in descending order.

Test case:

Alice 5
Bob 7
Bob John
John 113
Alice Cheryl
Daniel 13
John Alice
Alice 12


Result:

Alice 132
Daniel 13
Cheryl 5


Test case, suggested by Unrelated String:

Jimmy Adam
Donald 12
Amelia -11


Result:

Donald 12
Amelia -11


Test case:

CodegoLfer65 12
Bluman 9
CodegoLfer65 n4me
n4me -3


Result:

Bluman 9
n4me 9 (note: these two can be in either order)


Usernames are composed of only [A-Za-z0-9], i.e. alphanumerics. However, the input will not be a number in your language. This means usernames like 144, 015, 0x0F, 1e7 will not appear in the input. Numbers such as 1/3, 2.8 do not appear due to the character specification in the first place.

This question seems heavy on data structures, so golfing languages may not perform as well. Maybe other languages will have a fighting chance? Scoring is by lowest number of bytes, i.e. tag:code-golf .

• Can a user be instantiated by changing their name, without having gained rep? – Unrelated String Oct 29 '19 at 4:30
• Yes. I will add this test case. Note that the "old name" in that case really doesn't matter at all. – rigged Oct 29 '19 at 4:43
• May I assume rep change will never be zero? – tsh Oct 29 '19 at 7:36
• Suggested test case of Bob 5, Bob John, Bob 10. That should result in Bob 10, John 5, right? – Veskah Oct 29 '19 at 12:14
• Must names such as 1e4 and 0x123 be permitted? If so, there should probably be a test case such as Alice 1e4, Bob 0x123. A few of the answers treat 1e4 as a number. – Tesseract Oct 30 '19 at 16:44

# Python 3, 115 bytes

def f(l):
r={}
for n,m in l:q=m*0!=0;r[[n,m][q]]=r.pop(n,0)+[m,0][q]
return sorted(r.items(),key=lambda i:-i[1])


Try it online!

# Ruby, 102101959493 90 bytes

->c{l={};c.map{|a,b|x=l[a]||0;b*0!=0?(l[b],l[a]=x,p):l[a]=x+b};l.compact.sort_by{|a,b|-b}}


This takes input in the form of a list of ["name", "new_name"/score] pairs. And it outputs in an ordered list of ["name", score]

Golfy Tricks

• I used p with no args to get a nil value.
• I used String===b instead of b.is_a?String
• I used b!=b.ord b.ord!=b to find out if it's a string or not. See String.ord and Integer.ord
• I'm using b*0!=b to check if it's a string or not.
• For some reason, b.ord!=b means that I don't need a space between the ? ternary operator and the following (, but if I swap it to b!=b.ord, then another whitespace is needed.
• I sorted by -b, rather than by b and then reversing (d'oh!)

## Try it online!

Edit: Thanks to Veskah, for pointing out some problems this solution was missing. Somehow adding a temporary variable to hold l[a]||0 has shortened the code by a byte.

Edit: Thanks to G B, for suggesting b*0!=0 instead of b.ord!=b, which also allowed me to get rid of the space before the ?, saving 3 characters.

• @Veskah: Yep, good find. Forgot about the implication of just setting the default value, as I set the values to nil when the name changes. – IMP1 Oct 29 '19 at 14:22
• I also misunderstood that test case. Thanks for helping iron out the "edge cases" (read: cases that I just plain missed). – IMP1 Oct 29 '19 at 15:09
• use b*0!=0 to check if b is a string (-3 bytes) – G B Nov 5 '19 at 13:33

# PHP, 111 bytes

for(;$n=$argv[++$i];+$r&&$a[$n]+=$r)if(!+($r=$argv[++$i])&&$a[$r]=+$a[$n])unset($a[$n]);arsort($a);print_r($a);


Try it online!

Inputs are command arguments ($argv) as a plain list like: Alice, 5, Bob, 7. Every item is a separate argument. ### Commented for(;$n=$argv[++$i];    // $n is set to the first parameter (name) for each event +$r&&$a[$n]+=$r) // at the end of each iteration if$r is a number
//   add it to reputation of $n if( !+($r=$argv[++$i] // $r is set to the second event parameter (name or reputation change) )&&$a[$r]=+$a[$n]) // if$r is a name, transfer reputation of $n to$r
unset($a[$n]); //   and remove $n from$a
arsort($a); // after the loop, sort$a by its values in descending order
print_r($a); // print$a as string


# PHP, 98 bytes

This one shows non-existing users too (at the end of the list), but shows nothing as their reputation.

for(;$n=$argv[++$i];arsort($a))+($r=$argv[++$i])?$a[$n]+=$r:[$a[$r],$a[$n]]=[+$a[$n]];print_r($a);  Try it online! # Haskell, 253 bytes import Text.Read import Data.List m l(k,d)=maybe((d,o):n)(\a->(k,a+o):n)(readMaybe d)where o=maybe 0id(lookup k l);n=filter(\(x,_)->x/=k)l f v=sortBy(\(_,x)(_,y)->compare y x)$filter(\(x,y)->maybe("",0)id(find(\(a,_)->a==x)r)==(x,y))r where r=foldl m[]v


The function takes a list of String pairs and returns a list of (String, Int) pairs

## Full programme, 355 bytes

module Main where
import Data.List
m l[k,d]=maybe((d,o):n)(\a->(k,a+o):n)(readMaybe d)where o=maybe 0id(lookup k l);n=filter(\(x,_)->x/=k)l
main=getContents>>= \c->let r=foldl m[]$map words$lines c in putStr$unlines$map(\(x,y)->unwords[x,show y])$sortBy(\(_,x)(_,y)->compare y x)$filter(\(x,y)->maybe("",0)id(find(\(a,_)->a==x)r)==(x,y))r


The programme takes input as specified in the testcases via STDIN

# JavaScript (Node.js), 118 bytes

a=>a.flatMap(v=>([p,q]=v,u=a[p])?(q+-q?a[a[u[0]=q]=u,p]=0:u[1]+=q,[]):[q+-q?a[q]=[q,0]:a[p]=v]).sort((a,b)=>b[1]-a[1])


Try it online!

Expect input ([string, string]|[string, number])[] type.

q+-q is used to test whether q is a number or string. if q is number, q+-q will be falsy. If q is string, q+-q will be truthy.

• @Arnauld That won't work for string 0x1, so I had changed to something just testing number type vs. string type. – tsh Oct 31 '19 at 2:51
• Oh, I see. My bad. You should however fix your code in the footer so that it really works, e.g. for 1e4 instead of Alice in the 1st test case. – Arnauld Oct 31 '19 at 11:27

# AWK, 132 bytes

/.* -?[0-9]/{a[$1]+=$2}
!/.* -?[0-9]/{a[$2]=a[$1]+0;delete a[$1]} END{PROCINFO["sorted_in"]="@val_num_desc";for(k in a)print k,a[k]}  Try it online! We're careful to +0 to make sure the new name has reputation. The hardest part of this problem is printing the list out sorted by values. Thankfully, GNU Awk has an array called PROCINFO, and by writing the special value "@val_num_desc", the array will be numerically sorted in descending order by value. # Perl 5-a, 114 bytes $_=pop@F;/^-?\d+$/?$k{$F[0]}+=$_:($k{$_}=$k{$F[0]}+0,delete$k{$F[0]})}{say"$_$k{$_}"for sort{$k{$b}-$k{\$a}}keys%k


Try it online!

# Java 10, 166 251 bytes

import java.util.*;a->{var r=new HashMap<String,Long>();for(var l:a)if(l[1].matches("-?\\d+"))r.merge(l[0],new Long(l[1]),Long::sum);else{var t=r.remove(l[0]);r.put(l[1],t==null?0:t);}return r.entrySet().stream().sorted(Map.Entry.comparingByValue());}


+85 bytes because I hadn't noticed the output should be sorted by value.. >.>

Input as a 2D list of Strings, output as a sorted stream of key-value pairs.

Try it online.

Explanation:

import java.util.*;            // Required import for Map and HashMap
a->{                           // Method with 2D String-array as parameter
//  and a Stream of String-Long KeyValue-pairs as return-type
var r=new HashMap<String,Long>();
//  Map, starting empty
for(var l:a)                 //  Loop over the lines of the input:
if(l[1].matches("-?\\d+")) //   If the second part is an integer:
r.merge(l[0],            //    Use the first part as key
new Long(l[1]),  //    Convert the second part to an integer
Long::sum);      //    And sum it with a potential existing value for this key
else{                      //   Else (it's a rename):
var t=r.remove(l[0]);    //    Remove the first part from the map;
//    store the current value (of null if not present) in t
r.put(l[1],              //    Use the second part as new key
r.remove(l[0]));   //    And remove the first part from the map,
//    which will return the current value
//    (or null if not present)
return r                     //  Return the Map as result
.entrySet().stream()       //  after we've converted it to a steam of KeyValue pairs,
.sorted(Map.Entry.comparingByValue());}
//  and sorted it by value


# 05AB1E, 48 bytes

¯svÐ˜yUk;©di®èX.ïiX+‚ëy:}®ǝëX.ïiyëX¾‚}ª]Σθï}R


Input as a list of pairs.

Explanation:

¯                # Start with an empty list: []
sv              # Loop over each input-pair y
Ð             #  Triplicate the current list
˜            #  Flatten it
y          #  Push the key and value of the current item y separated to the stack
U         #  Pop the value, and store it in variable X
k         #  Get the index of the key in the flattened list
;©       #  Halve it, and store it in variable ® (without popping)
di           #  If this index/2 is non-negative (>= 0):
®è         #   Get the ®'th pair from the current list
X.ïi     #   If X is an integer:
    #    Push the key and value of the indexed pair separated to the stack
X+  #    Add X to the value
‚ #    And pair the key and this new value back together
ë     #   Else (it's a rename):
y   #    Push the key and value of the current item y separated to the stack
:  #    And replace key with value in the indexed pair
}     #   After the if-else:
®ǝ   #   Insert this modified pair at index ® back into the list
ë           #  Else (the current key doesn't exist yet):
X.ïi       #   If X is an integer:
y      #    Simply use the current item y
ë       #   Else:
X¾‚    #    Pair X with 0
}       #   After the if-else:
ª      #   Append this new pair to the list
]              # After the loop:
Σ  }          # Sort the resulting list in ascending order by:
θ            #  The last item of each pair (the values)
ï           #  Casted to an integer (because the : might have made it a string)
R         # And then reverse this sorted list to make it descending
# (after which it is output implicitly as result)