www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to imporve D-translation of these Python list comprehensions ?

reply xenon325 <anm programmer.net> writes:
A workmate has recently shown this piece of code to show how nice 
Python is (we are mostly C and growing C++ shop):

     import json
     from itertools import chain

     srv1 = {'acs': {'ver': '1.2.3', 'rev': '6f2260d'}, 'cms': 
{'ver': '4.5', 'rev': 'b17a67e'}, 'ots': {'ver': '6.7.80', 'rev': 
'4f487d2'}}
     srv2 = {'acs': {'ver': '1.2.3', 'rev': '6f2260d'}, 'cms': 
{'ver': '5.1', 'rev': '2a56c53'}, 'vaa': {'ver':    '0.7', 'rev': 
'00852cb'}}


     def aget(d, k1, k2):
         return d.get(k1, {}).get(k2, '')

     aa = ['ver', 'rev']
     kk = set(chain(srv1.keys(), srv2.keys()))
     dd = [dict(_name=k, **{a + str(i): aget(d, k, a) for a in aa 
for i, d in enumerate([srv1, srv2])}) for k in sorted(kk)]

     print(json.dumps(dd, indent=2, sort_keys=True))


Output is:

     [
       {
         "_name": "acs",
         "rev0": "6f2260d",
         "rev1": "6f2260d",
         "ver0": "1.2.3",
         "ver1": "1.2.3"
       },
       {
         "_name": "cms",
         "rev0": "b17a67e",
         "rev1": "2a56c53",
         "ver0": "4.5",
         "ver1": "5.1"
       },
       {
         "_name": "ots",
         "rev0": "4f487d2",
         "rev1": "",
         "ver0": "6.7.80",
         "ver1": ""
       },
       {
         "_name": "vaa",
         "rev0": "",
         "rev1": "00852cb",
         "ver0": "",
         "ver1": "0.7"
     ]
       }

Another coworker replied with Scala equivalent. So I thought, why 
wouldn't I join the party and translate that to D :)

My best take so far is (with some D-intro comments 
https://run.dlang.io/is/GxsauU):

     import std.stdio;
     import std.algorithm;
     import std.range;
     import std.array;
     import std.conv;
     import std.json;

     void main()
     {
         // not `immutable` to describe one less thing
         const srv1 = [
             "acs": ["ver": "1.2.3", "rev": "6f2260d"],
             "cms": ["ver": "4.5", "rev": "b17a67e"],
             "ots": ["ver": "6.7.80", "rev": "4f487d2"]];
         const srv2 = [
             "acs": ["ver": "1.2.3", "rev": "6f2260d"],
             "cms": ["ver": "5.1", "rev": "2a56c53"],
             "vaa": ["ver":    "0.7", "rev": "00852cb"]];

         string[string][] result;
         chain(srv1.keys, srv2.keys)
             .sort 					
             .uniq 					
             .each!( (uniqComp) {	
                 auto verInfo = ["_name": uniqComp];
                 [srv1, srv2]
                     .enumerate
                     .each!( serv =>
                             ["ver", "rev"].each!( prop =>
                                   verInfo[prop ~ 
serv.index.to!string]
                                   = serv.value.get(uniqComp, ["": 
""]).get(prop, ""))
                     );
                 /+ // The same as above and I like this better, 
actually
                 foreach(servIdx, serv; [srv1, srv2].enumerate){
                     foreach(prop; ["ver", "rev"])
                         verInfo[prop ~ servIdx.to!string]
                         = serv.get(uniqComp, ["": ""]).get(prop, 
"");
                 } +/

                 result ~= verInfo;
             });
         writeln("---");
         writeln(JSONValue(result).toPrettyString());
         writeln("---");
     }

I think, most clear code would be with tripple `foreach`, so I'll 
go with that. But probably someone will come up with something 
better and range-ier.

Suggestion are welcome!

---
Alexander
Jan 15
next sibling parent reply Igor Shirkalin <mathsoft inbox.ru> writes:
On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
 A workmate has recently shown this piece of code to show how 
 nice Python is (we are mostly C and growing C++ shop):
     dd = [dict(_name=k, **{a + str(i): aget(d, k, a) for a in 
 aa for i, d in enumerate([srv1, srv2])}) for k in sorted(kk)]
This is the most terrible Python code I have ever seen. If you know Python, could you please unroll to more readable form?
 Suggestion are welcome!
 ---
 Alexander
Jan 15
parent Russel Winder <russel winder.org.uk> writes:
On Mon, 2018-01-15 at 20:27 +0000, Igor Shirkalin via Digitalmars-d-
learn wrote:
 On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
 A workmate has recently shown this piece of code to show how=20
 nice Python is (we are mostly C and growing C++ shop):
     dd =3D [dict(_name=3Dk, **{a + str(i): aget(d, k, a) for a in=20
 aa for i, d in enumerate([srv1, srv2])}) for k in sorted(kk)]
=20 This is the most terrible Python code I have ever seen. If you know Python, could you please unroll to more readable form?
Actually I have seen worse, but=E2=80=A6 I think the way this Python code i= s presented is fairly dreadful. I am fiddling with trying to provide a better version. If people are interested in this I can share in a while when it's ready. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Road m: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk
Feb 01
prev sibling next sibling parent reply lobo <swamp.lobo gmail.com> writes:
On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
 A workmate has recently shown this piece of code to show how 
 nice Python is (we are mostly C and growing C++ shop):

 [...]
Well if that is what they can do in Python I'd hate to see their C++! They have done a great job making Python code read like Perl. This looks like something you'd see in an assignment at uni; "Decipher this Python code and port to your language of choice" :)
Jan 15
parent reply Russel Winder <russel winder.org.uk> writes:
On Mon, 2018-01-15 at 21:13 +0000, lobo via Digitalmars-d-learn wrote:
 On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
 A workmate has recently shown this piece of code to show how=20
 nice Python is (we are mostly C and growing C++ shop):
=20
 [...]
=20 Well if that is what they can do in Python I'd hate to see their=20 C++! They have done a great job making Python code read like=20 Perl. This looks like something you'd see in an assignment at=20 uni; "Decipher this Python code and port to your language of=20 choice" :)
Apart from the slur on Perl, I have to agree with this. Using a dreadful bit of Python code is the start of a downward spiral of expectation and programming in other languages. As noted earlier, I am working on a nicer rendering of this algorithm, and will be happy to share if people are interested. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Road m: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk
Feb 01
parent bauss <jj_1337 live.dk> writes:
On Thursday, 1 February 2018 at 11:59:23 UTC, Russel Winder wrote:
 On Mon, 2018-01-15 at 21:13 +0000, lobo via Digitalmars-d-learn 
 wrote:
 On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
 A workmate has recently shown this piece of code to show how 
 nice Python is (we are mostly C and growing C++ shop):
 
 [...]
Well if that is what they can do in Python I'd hate to see their C++! They have done a great job making Python code read like Perl. This looks like something you'd see in an assignment at uni; "Decipher this Python code and port to your language of choice" :)
Apart from the slur on Perl, I have to agree with this. Using a dreadful bit of Python code is the start of a downward spiral of expectation and programming in other languages. As noted earlier, I am working on a nicer rendering of this algorithm, and will be happy to share if people are interested.
I'd be interested in seeing the result.
Feb 01
prev sibling next sibling parent Biotronic <simen.kjaras gmail.com> writes:
On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
 I think, most clear code would be with tripple `foreach`, so 
 I'll go with that. But probably someone will come up with 
 something better and range-ier.
I will admit clarity has suffered, but I like the brevity: import std.json : JSONValue; import std.array : array, assocArray; import std.range : enumerate, byPair; import std.algorithm : sort, joiner, map, uniq, each; import std.typecons : tuple; import std.conv : to; import std.stdio : writeln; unittest { immutable srv1 = ["acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "4.5", "rev": "b17a67e"], "ots": ["ver": "6.7.80", "rev": "4f487d2"]]; immutable srv2 = ["acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "5.1", "rev": "2a56c53"], "vaa": ["ver": "0.7", "rev": "00852cb"]]; immutable keys = ["rev", "ver"]; immutable srvs = [srv1, srv2]; alias aget = (name, key) => srvs.map!(s => s.get(name, [key:""])[key]); alias bget = (name, key) => aget(name, key).enumerate.map!(b => tuple(key~b.index.to!string, b.value)); alias merge = (aa1, aa2) => (aa2.byPair.each!(kv => aa1[kv.key] = kv.value), aa1); auto result = srvs .map!(s => s.byKey) .joiner .array .sort .uniq .map!(name => merge(keys.map!(key => bget(name, key)).joiner.assocArray, ["_name": name])) .array; writeln(JSONValue(result).toPrettyString()); } -- Simen
Jan 15
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.01.2018 20:05, xenon325 wrote:
 
 
 I think, most clear code would be with tripple `foreach`, so I'll go 
 with that. But probably someone will come up with something better and 
 range-ier.
 
 Suggestion are welcome!
import std.stdio, std.algorithm, std.range, std.array, std.conv, std.json, std.typecons; auto aa(R)(R r){ typeof(r.front[1])[typeof(r.front[0])] a; foreach(x;r) a[x[0]] = x[1]; return a; } alias emap(alias a) = map!(x => a(x.expand)); void main(){ auto srv1 = [ "acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "4.5", "rev": "b17a67e"], "ots": ["ver": "6.7.80", "rev": "4f487d2"]]; auto srv2 = [ "acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "5.1", "rev": "2a56c53"], "vaa": ["ver": "0.7", "rev": "00852cb"]]; chain(srv1.keys, srv2.keys).sort.uniq .map!(k => chain(only(tuple("_name", k)), cartesianProduct(only("ver", "rev"), only(srv1, srv2).enumerate) .emap!((prop, srv) => tuple(text(prop, srv.index), srv.value.get(k, null).get(prop, ""))) ).aa).array.JSONValue.toPrettyString.writeln; }
Jan 15
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 15.01.2018 22:51, Timon Gehr wrote:
 auto aa(R)(R r){
      typeof(r.front[1])[typeof(r.front[0])] a;
      foreach(x;r) a[x[0]] = x[1];
      return a;
 }
Actually, better to use std.array.assocArray. import std.stdio, std.algorithm, std.range, std.array, std.conv, std.json, std.typecons; alias emap(alias a) = map!(x => a(x.expand)); void main(){ auto srv1 = [ "acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "4.5", "rev": "b17a67e"], "ots": ["ver": "6.7.80", "rev": "4f487d2"]]; auto srv2 = [ "acs": ["ver": "1.2.3", "rev": "6f2260d"], "cms": ["ver": "5.1", "rev": "2a56c53"], "vaa": ["ver": "0.7", "rev": "00852cb"]]; chain(srv1.keys, srv2.keys).sort.uniq .map!(k => chain(only(tuple("_name", k)), cartesianProduct(only("ver", "rev"), only(srv1, srv2).enumerate) .emap!((prop, srv) => tuple(text(prop, srv.index), srv.value.get(k, null).get(prop, ""))) ).assocArray).array.JSONValue.toPrettyString.writeln; }
Jan 15
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
     def aget(d, k1, k2):
         return d.get(k1, {}).get(k2, '')

     aa = ['ver', 'rev']
     kk = set(chain(srv1.keys(), srv2.keys()))
     dd = [dict(_name=k, **{a + str(i): aget(d, k, a) for a in 
 aa for i, d in enumerate([srv1, srv2])}) for k in sorted(kk)]
But that was really verbose, why didn't he contract everything into one line? dd = [dict(_name=k, **{a + str(i): d.get(k,{}).get(a,'') for a in ('ver','rev') for i, d in enumerate((srv1, srv2))}) for k in sorted(set(chain(srv1.keys(), srv2.keys())))] :*)
Feb 01
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 1 February 2018 at 20:18:37 UTC, Ola Fosheim Grøstad 
wrote:
 On Monday, 15 January 2018 at 19:05:52 UTC, xenon325 wrote:
     def aget(d, k1, k2):
         return d.get(k1, {}).get(k2, '')

     aa = ['ver', 'rev']
     kk = set(chain(srv1.keys(), srv2.keys()))
     dd = [dict(_name=k, **{a + str(i): aget(d, k, a) for a in 
 aa for i, d in enumerate([srv1, srv2])}) for k in sorted(kk)]
But that was really verbose, why didn't he contract everything into one line? dd = [dict(_name=k, **{a + str(i): d.get(k,{}).get(a,'') for a in ('ver','rev') for i, d in enumerate((srv1, srv2))}) for k in sorted(set(chain(srv1.keys(), srv2.keys())))] :*)
Rewritten in without comprehensions it is something like: dd = [] for k in sorted(set(chain(srv1.keys(), srv2.keys()))): record = {'_name':k} for a in 'ver','rev': for i, d in enumerate((srv1, srv2)): record[f'{a}{i}'] = d.get(k,{}).get(a,'') dd.append(record)
Feb 01