开发者

Household mail merge (code golf)

I wrote some mail merge code the other day and although it works I'm a turned off by the code. I'd like to see what it would look like in other languages.

So for the input the routine takes a list of contacts

Jim,Smith,2681 Eagle Peak,,Bellevue,Washington,United States,98004
Erica,Johnson,2681 Eagle Peak,,Bellevue,Washington,United States,98004
Abraham,Johnson,2681 Eagle Peak,,Bellevue,Washington,United States,98004开发者_JAVA百科
Marge,Simpson,6388 Lake City Way,,Burnaby,British Columbia,Canada,V5A 3A6
Larry,Lyon,52560 Free Street,,Toronto,Ontario,Canada,M4B 1V7
Ted,Simpson,6388 Lake City Way,,Burnaby,British Columbia,Canada,V5A 3A6
Raoul,Simpson,6388 Lake City Way,,Burnaby,British Columbia,Canada,V5A 3A6

It will then merge lines with the same address and surname into one record. Assume the rows are unsorted). The code should also be flexible enough that fields can be supplied in any order (so it will need to take field indexes as parameters). For a family of two it concatenates both first name fields. For a family of three or more the first name is set to "the" and the lastname is set to "surname family".

Erica and Abraham,Johnson,2681 Eagle Peak,,Bellevue,Washington,United States,98004
Larry,Lyon,52560 Free Street,,Toronto,Ontario,Canada,M4B 1V7
The,Simpson Family,6388 Lake City Way,,Burnaby,British Columbia,Canada,V5A 3A6
Jim,Smith,2681 Eagle Peak,,Bellevue,Washington,United States,98004

My C# implementation of this is:

var source = File.ReadAllLines(@"sample.csv").Select(l => l.Split(','));
var merged = HouseholdMerge(source, 0, 1, new[] {1, 2, 3, 4, 5});

public static IEnumerable<string[]> HouseholdMerge(IEnumerable<string[]> data, int fnIndex, int lnIndex, int[] groupIndexes)
{            
    Func<string[], string> groupby = fields => String.Join("", fields.Where((f, i) => groupIndexes.Contains(i)));

    var groups = data.OrderBy(groupby).GroupBy(groupby);

    foreach (var group in groups)
    {
        string[] result = group.First().ToArray();

        if (group.Count() == 2)
        {
            result[fnIndex] += " and " + group.ElementAt(1)[fnIndex];
        }
        else if (group.Count() > 2)
        {
            result[fnIndex] = "The";
            result[lnIndex] += " Family";
        }

        yield return result;
    }            
}

I don't like how I've had to do the groupby delegate. I'd like if C# had some way to convert a string expression to a delegate. e.g. Func groupby = f => "f[2] + f[3] + f[4] + f[5] + f[1];" I have a feeling something like this can probably be done in Lisp or Python. I look forward to seeing nicer implementation in other languages.

Edit: Where did the community wiki checkbox go? Some mod please fix that.


Ruby — 181 155

Name/surname indexes are in code:a and b. Input data is from ARGF.

a,b=0,1
[*$<].map{|i|i.strip.split ?,}.group_by{|i|i.rotate(a).drop 1}.map{|i,j|k,l,m=j
k[a]+=' and '+l[a]if l
(k[a]='The';k[b]+=' Family')if m
puts k*','}


Python - not golfed

I'm not sure what the order of the rows should be if the indices are not 0 and 1 for the input file

import csv
from collections import defaultdict

class HouseHold(list):
    def __init__(self, fn_idx, ln_idx):
        self.fn_idx = fn_idx
        self.ln_idx = ln_idx

    def append(self, item):
        self.item = item
        list.append(self, item[self.fn_idx])

    def get_value(self):
        fn_idx = self.fn_idx
        ln_idx = self.ln_idx
        item = self.item
        addr = [j for i,j in enumerate(item) if i not in (fn_idx, ln_idx)]
        if len(self) < 3:
            fn, ln = " and ".join(self), item[ln_idx]
        else:
            fn, ln = "The", item[ln_idx]+" Family"
        return [fn, ln] + addr

def source(fname):
    with open(fname) as in_file:
        for item in csv.reader(in_file):
            yield item

def household_merge(src, fn_idx, ln_idx, groupby):
    res = defaultdict(lambda:HouseHold(fn_idx, ln_idx))
    for item in src:
        key = tuple(item[x] for x in groupby)
        res[key].append(item)
    return res.values()

data =  household_merge(source("sample.csv"), 0, 1, [1,2,3,4,5,6,7])
with open("result.csv", "w") as out_file:
    csv.writer(out_file).writerows(item.get_value() for item in data)


Python - 178 chars

import sys
d={}
for x in sys.stdin:F,c,A=x.partition(',');d[A]=d.get(A,[])+[F]
print"".join([" and ".join(v)+c+A,"The"+c+A.replace(c,' Family,',1)][2<len(v)]for A,v in d.items())

Output

Jim,Smith,2681 Eagle Peak,,Bellevue,Washington,United States,98004
The,Simpson Family,6388 Lake City Way,,Burnaby,British Columbia,Canada,V5A 3A6
Larry,Lyon,52560 Free Street,,Toronto,Ontario,Canada,M4B 1V7
Erica and Abraham,Johnson,2681 Eagle Peak,,Bellevue,Washington,United States,98004


Python 2.6.6 - 287 Characters

This assumes you can hard code a filename (named i). If you want to take input from command line, this goes up ~16 chars.

from itertools import*
for z,g in groupby(sorted([l.split(',')for l in open('i').readlines()],key=lambda x:x[1:]), lambda x:x[2:]):
 l=list(g);r=len(l);k=','.join(z);o=l[0]
 if r>2:print'The,'+o[1],"Family,"+k,
 elif r>1:print o[0],"and",l[1][0]+","+o[1]+","+k,
 else:print','.join(o),

Output

Erica and Abraham,Johnson,2681 Eagle Peak,,Bellevue,Washington,United States,98004
Larry,Lyon,52560 Free Street,,Toronto,Ontario,Canada,M4B 1V7
The,Simpson Family,6388 Lake City Way,,Burnaby,British Columbia,Canada,V5A 3A6
Jim,Smith,2681 Eagle Peak,,Bellevue,Washington,United States,98004

I'm sure this could be improved upon, but it is getting late.


Haskell - 341 321

(Changes as per comments).

Unfortunately Haskell has no standard split function which makes this rather long.

Input to stdin, output on stdout.

import List
import Data.Ord
main=interact$unlines.e.lines
s[]=[]
s(',':x)=s x
s l@(x:y)=let(h,i)=break(==k)l in h:(s i)
t[]=[]
t x=tail x
h=head
m=map
k=','
e l=m(t.(>>=(k:)))$(m c$groupBy g$sortBy(comparing t)$m s l)
c(x:[])=x
c(x:y:[])=(h x++" and "++h y):t x
c x="The":((h$t$h x)++" Family"):(t$t$h x)
g a b=t a==t b


Lua, 434 bytes

x,y=1,2 s,p,r,a=string.gsub,pairs,io.read,{}for j,b,c,d,e,f,g,h,i in r('*a'):gmatch('('..('([^,]*),'):rep(7)..'([^,]*))\n')
do k=s(s(s(j,b,''),c,''),'[,%s]','')for l,m in p(a)do if not m.f and (m[y]:match(c) and m[9]==k) then z=1
if m.d then m[x]="The"m[y]=m[y]..' family'm.f=1 else m[x]=m[x].." and "..b m.d=1 end end end if not z then
a[#a+1]={b,c,d,e,f,g,h,i,k} end z=nil end for k,v in p(a)do v[9]=nil print(table.concat(v,','))end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜