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 2018
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 2018
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 2018
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 2018
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 2018
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 2018
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 2018
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 2018
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 2018
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 2018
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 2018