lainchan / λ

(untitled)

Junichi Fujiwara · 2017-09-15 03:02
Sup / λ / I was thinking it might be fun to have a programing challenge, learn about data structures and stuff. I know we have some wizards here, but I though I'd start with this one and see how fast you guy can solve it. values = [ (23, 'i7-7700', 289.99, 5), (24, 'i7-6850K', 329.99, 2), (25, 'i7-6900', 929.99, 5), (26, 'i7-6950X', 1649.99, 3), ] keys = ['id', 'name', 'price', 'qty'] So the end goal is to format this data into a JSON object ready to be consumed by a client. There are some super easy ways to do it, but the goal is to do it with as little code as possible. The values given are in Python, but feel free to use whatever language you want.
XERXES · 2017-09-15 05:43
>>3508 Since noone else seems to be biting, here goes: (defparameter vals '((23 "i7-7700" 289.99 5) (24 "i7-6850K" 329.99 2) (25 "i7-6900" 929.99 5) (26 "i7-6950X" 1649.99 3))) (defparameter keys '("id" "name" "price" "qty")) (format t ((lambda (j) (format nil "[~{{~A~^}, ~}]" j)) (mapcar (lambda (u) (format nil "~{~A~^, ~}" (mapcar (lambda (tu) (format nil "~S: ~S" (first tu) (second tu))) u))) (mapcar (lambda (v) (mapcar #'list keys v)) vals))))
Cypher · 2017-09-15 11:31
Are we allowed to use libraries for JSON serialization or do you have to roll your own? values = [ [ 23, 'i7-7700', 289.99, 5 ], [ 24, '17-6850K', 329.99, 2 ], [ 25, 'i7-6900', 929.99, 5 ], [ 26, 'i7-6950X', 1649.99, 3 ] ] keys = ['id', 'name', 'price', 'qty'] obj = values.map { |col| Hash[[ keys, col ].transpose] } # Library require 'json' obj.to_json # manual def json_serialize val case val when String then val.inspect when Hash '{%s}' % val.map do |k, v| json_serialize(k) + ?: + json_serialize(v) end.join(',') when Array '[%s]' % val.map do |x| json_serialize(x) end.join(',') when Integer, Float, true, false then val.to_s else val.to_s.inspect end end json_serialize obj >>3512 I'm pretty sure that doesn't output valid json, everything that's not a number or a boolean should be double quoted.
push · 2017-09-15 15:16
>>3508 A C solution that prints the most compact representation: #include <stdio.h> #define countof(ary) (sizeof(ary) / sizeof((ary)[0])) int main(void) { const struct { int id; const char * name; double price; int qty; } values[] = { { 23, "i7-7700", 289.99, 5 }, { 24, "i7-6850K", 329.99, 2 }, { 25, "i7-6900", 929.99, 5 }, { 26, "i7-6950X", 1649.99, 3 } }; const char * const keys[] = { "id", "name", "price", "qty" }; const char * prefix = ""; printf("["); for(unsigned i = 0; i < countof(values); i++) { printf("%s{\"%s\":%d,\"%s\":\"%s\",\"%s\":%.2f,\"%s\":%d}", prefix, keys[0], values[i].id, keys[1], values[i].name, keys[2], values[i].price, keys[3], values[i].qty); prefix = ","; } printf("]"); } A bit inelegant, but I think this challenge is geared more towards dynamically-typed languages.
Bob Arctor · 2017-09-15 15:44
>>3512 lisp >>3519 ruby >>3523 its pretty elegant for C! All great solutions lains. >>3519 Sorry for not clarifying more on this. I think a good way to approach it is as long as it outputs a JSON like key value data structure that could easily be serialized Here is a pretty basic Python take on it items = [ dict(zip([key for key in keys], row)) for row in values ]
array · 2017-09-15 20:05
>>3525 >zip([key for key in keys], row) Just do zip (keys, row), or Lain will be sad.
Dr. X · 2017-09-16 00:11
>>3527 thanks, nice catch
Anonymous · 2017-09-24 06:46
#include <stdio.h> typedef struct item { int id; const char *name; float price; int qty; } item; #define ITEMC 4 static int format_item(item, const char *, char *); #define BUFLEN 128 #define $nprintf(buf, len, offset, level, fmt, ...) \ offset += snprintf(buf + offset, len - offset, "%*s" fmt, level * 4, \ "", ##__VA_ARGS__); \ if (offset > BUFLEN -1) { \ return -1; \ } int main() { char buf[BUFLEN]; item items[ITEMC] = { {23, "i7-7700", 289.99, 5}, {24, "i7-6850K", 329.99, 2}, {25, "i7-6900", 929.99, 5}, {26, "i7-6950X", 1649.99, 3}, }; const char *prefix = "\n "; printf("{"); for (unsigned i = 0; i < ITEMC; i++) { printf("%s", format_item(items[i], prefix, buf) ? (fprintf(stderr, "Invalid input\n"), "") : (prefix = ",", buf)); } printf("\n}\n"); return 0; } int format_item(item item, const char *prefix, char *buf) { int offset = 0; offset += snprintf(buf, BUFLEN, "%s{\n", prefix); $nprintf(buf, BUFLEN, offset, 2, "\"id\": %d,\n", item.id); $nprintf(buf, BUFLEN, offset, 2, "\"name\": \"%s\",\n", item.name); $nprintf(buf, BUFLEN, offset, 2, "\"price\": %.2f,\n", item.price); $nprintf(buf, BUFLEN, offset, 2, "\"qty\": %d\n", item.qty); $nprintf(buf, BUFLEN, offset, 1, "}"); return 0; } outputs to stdout with indentation went for the shortest possible solution that doesn't sacrifice readability/theoretical maintainability/error handling
XERXES · 2017-09-24 10:46
Here's some APL I wrote over the course of five minutes or so for this challenge. Do note that I didn't put any thought into optimizing this beyond fitting it on one line: values←4 4⍴23 'i7-7700' 289.99 5 24 'i7-6850K' 329.99 2 25 'i7-6900' 929.99 5 26 'i7-6950X' 1649.99 3 keys←'id' 'name' 'price' 'qty' ('{',,(('{',(4 4⍴'"'),¨(4 4⍴keys),¨(4 4⍴⊂'":'),¨(values[;⎕IO (⎕IO+1) (⎕IO+2)],¨','),values[;⎕IO+3]),'}')),'}' This produces the following: {{ "id": 23 , "name":i7-7700, "price": 289.99 , "qty": 5 }{ "id": 24 , "name":i7-6850K, "price": 329.99 , "qty": 2 }{ "id": 25 , "name":i7-6900, "price": 929.99 , "qty": 5 }{ "id": 26 , "name":i7-6950X, "price": 1649.99 , "qty": 3 }} It took extra work to remove trailing commas after the "qty" value, which is the reason for all of the ⎕IO, as I simply split the array, add the commas, and then join it back. To be brief, this appends '}' to the result of prefixing with '{' the flattened result of appending '}' to each row of an array after having each row prefixed by '{' arrived at by joining the last row of values with the other rows having had ',' appended to the elements and each element then being prefixed by '":' and then the keys and then '"', arriving at the JSON. This JSON isn't as efficient as it could be, as removing every space would then produce what I believe is the optimal representation, but I'm not currently going to do this.
Erik Johansson · 2017-09-24 11:28
>>3702 I noticed that the strings aren't double quoted, so I've corrected this. A simple solution is modifying values beforehand with the following: values←values[;⎕IO],('"',¨values[;⎕IO+1],¨'"'),values[;(⎕IO+2) (⎕IO+3)]◊ This has the entire program being the following: values←values[;⎕IO],('"',¨values[;⎕IO+1],¨'"'),values[;(⎕IO+2) (⎕IO+3)]◊('{',,(('{',(4 4⍴'"'),¨(4 4⍴keys),¨(4 4⍴⊂'":'),¨(values[;⎕IO (⎕IO+1) (⎕IO+2)],¨','),values[;⎕IO+3]),'}')),'}' If the program needed to run more than once, an intermediate value could be assigned to, instead, but that's not here. I've also optimized the representation a tad by removing some spacing with ' '~⍨∊; so, the current line comes to this: values←values[;⎕IO],('"',¨values[;⎕IO+1],¨'"'),values[;(⎕IO+2) (⎕IO+3)]◊' '~⍨∊(,('{',,(('{',(4 4⍴'"'),¨(4 4⍴keys),¨(4 4⍴⊂'":'),¨(values[;⎕IO (⎕IO+1) (⎕IO+2)],¨','),values[;⎕IO+3]),'}')),'}') This is the output: {{"id": 23 ,"name":"i7-7700","price": 289.99 ,"qty": 5 }{"id": 24 ,"name":"i7-6850K","price": 329.99 ,"qty": 2 }{"id": 25 ,"name":"i7-6900","price": 929.99 ,"qty": 5 }{"id": 26 ,"name":"i7-6950X","price": 1649.99 ,"qty": 3 }} To add to the explanation, the values array merely has its second column encompassed by double quotes, beforehand, and the final array is merely untangled further and all spaces in this intermediate array are removed by the convenient without function, ~.
HELIOS · 2017-09-24 12:05
type v = I of int | F of float | S of string let values = [[I 23; S "i7-7700";F 289.99;I 5]; [I 24;S "i7-6850K";F 329.99;I 2]; [I 25;S "i7-6900";F 929.99;I 5]; [I 26;S "i7-6950X";F 1649.99;I 3]] let keys = ["id"; "name"; "price"; "qty"] let format_pair a b = "\"" ^ a ^ "\":"^ (function | I i -> string_of_int i | S s -> "\"" ^ s ^ "\"" | F f -> string_of_float f) b let fold b e l = b ^ List.fold_left (fun a b -> a^","^b) (List.hd l) (List.tl l) ^ e let res k v = fold "[" "]" @@ List.map (fold "{" "}") @@ List.map (List.map2 format_pair k) v let () = print_endline (res keys values) ocaml has a strong static type system, so I declared a type v for values. I have two helper functions format_pair (so that format_pair a b returns "a":b), and fold (for unfolding a list into a string). The () function in ocaml is similar to the main function in C (except that it has no argument at all). I put it so you have an execution of the code.
null · 2017-09-24 21:04
>>3703 So, I took a break and now I've greatly simplified what I wrote earlier; this is ninety-five characters in total, with the first six not being strictly necessary: ' '~⍨∊'}',⍨'{',,'}',⍨'{',(4 4⍴'"',¨keys,¨⊂'":'),¨(4 4⍴' " '),¨(values,¨4 4⍴' " '),¨4 4⍴',,, ' Removing parenthesis with the ⍨ operator is always fun. To explain, a four by four array containing commas in all but the last column is created and appended to the result of joining the values array with a four by four array containing spaces in all columns but the second, which contains double quotes, and this intermediate array is then prefixed with an identical space and double quote array; this then does with keys roughly the same as with values, enclosing it within a double quote and a double quote followed by a colon and then resizing it for values, before prefixing it to such; the last bit is where I eliminated a great deal of the parenthesis, prefixing each row by an opening brace, appending a closing brace to each row with ⍨, flattening the array into a vector, prefixing the new whole with an opening brace, appending a closing brace to the new whole, flattening it greatly with ∊ and then removing spaces using the without function, ~. If you're wondering, I wrote this ``inside out'', by considering what I'd need do to each individual element first and moving on to the eventual whole. It's very fun to be at the interpreter playing around with pieces and then joining them when each is correct and right. This is the final result: {{"id": 23 ,"name":"i7-7700","price": 289.99 ,"qty": 5 }{"id": 24 ,"name":"i7-6850K","price": 329.99 ,"qty": 2 }{"id": 25 ,"name":"i7-6900","price": 929.99 ,"qty": 5 }{"id": 26 ,"name":"i7-6950X","price": 1649.99 ,"qty": 3 }}
Dixie Flatline · 2017-09-24 22:53
>>3508 How's this? #ruby values.map{|v| keys.zip(v).flatten}.map{|h| Hash[*h]} #output => [{"id"=>23, "name"=>"i7-7700", "price"=>289.99, "qty"=>5}, {"id"=>24, "name"=>"i7-6850K", "price"=>329.99, "qty"=>2}, {"id"=>25, "name"=>"i7-6900", "price"=>929.99, "qty"=>5}, {"id"=>26, "name"=>"i7-6950X", "price"=>1649.99, "qty"=>3}] require 'benchmark' 10.times {puts Benchmark.measure{values.map{|v| keys.zip(v).flatten}.map{|h| Hash[*h]}}} 0.000000 0.000000 0.000000 ( 0.000038) 0.000000 0.000000 0.000000 ( 0.000028) 0.000000 0.000000 0.000000 ( 0.000043) 0.000000 0.000000 0.000000 ( 0.000026) 0.000000 0.000000 0.000000 ( 0.000027) 0.000000 0.000000 0.000000 ( 0.000051) 0.000000 0.000000 0.000000 ( 0.000026) 0.000000 0.000000 0.000000 ( 0.000042) 0.000000 0.000000 0.000000 ( 0.000032) 0.000000 0.000000 0.000000 ( 0.000042)
dictionary · 2017-09-25 06:15
enum Value { Int(i32), Float(f32), Str(&'static str), } fn fmt_pair(key: &str, value: &Value) -> String { match value { &Value::Int(i) => format!("\"{}\":{}", key, i), &Value::Float(f) => format!("\"{}\":{:.2}", key, f), &Value::Str(s) => format!("\"{}\":\"{}\"", key, s), } } const KEYS: [&'static str; 4] = ["id", "name", "price", "qty"]; const VALUES: [[Value; 4]; 4] = [ [Value::Int(23), Value::Str("i7-7700"), Value::Float(289.99), Value::Int(5)], [Value::Int(24), Value::Str("i7-6850K"), Value::Float(329.99), Value::Int(2)], [Value::Int(25), Value::Str("i7-6900"), Value::Float(929.99), Value::Int(5)], [Value::Int(26), Value::Str("i7-6950X"), Value::Float(1649.99), Value::Int(3)], ]; fn main() { let s = VALUES.iter().fold(String::from(""), |a, v| { let s1 = KEYS.iter().zip(v.iter()).fold(String::from(""), |a1, k| { format!("{}{},", a1, fmt_pair(k.0, k.1)) }); format!("{}{{{}}},", a, s1.get(..s1.len() - 1).unwrap()) }); println!("{{{}}}", s.get(..s.len() - 1).unwrap()); } this is an annoying exercise for a statically typed language but it's not too bad in rust
Yet Another Lainon · 2017-09-26 10:12
C++ #include <stdio.h> #include <string.h> #define ITEM_TABLE \ ENTRY(idx, int, id, ", ") \ ENTRY(idx, char*, name, ", ") \ ENTRY(idx, float, price, ", ") \ ENTRY(idx, int, qty, "}\n") #define GET_TYPE(type) strcmp(#type, "int") == 0 ? "%d" : (strcmp(#type, "float") == 0 ? "%.2f" : "\"%s\"") #define GET_FORMAT(type) "%d: " ## GET_TYPE(type) ## "\n" int main() { const char* comma = ""; struct { #define ENTRY(idx, x, y, end) x y; ITEM_TABLE #undef ENTRY } values[] = { {23, "i7-7700", 289.99, 5}, {24, "i7-6850K", 329.99, 2}, {25, "i7-6900", 929.99, 5}, {26, "i7-6950X", 1649.99, 3} }; #define ENTRY(idx, x, y, end) \ printf("\"%s\": ", #y); \ printf(GET_TYPE(x), values[idx].y); \ printf(end); printf("["); for(int idx = 0; idx < sizeof(values) / sizeof(values[0]); idx++) { printf("%s{", comma); ITEM_TABLE comma = ","; } printf("]\n"); #undef ENTRY return 0; } Was playing lately with MACRO X in C so I thought this could be a exercises. The cool thing about this is even if the structure will change the "for loop" will stay the same. Only the ITEM_TABLE and values would had to be modified.
x<->y · 2017-09-29 14:26
Still learing python but I have this: if __name__ == "__main__": keys = ["id", "name", "price", "qty"] values = [[23, "i7-7700", 289.99, 5], [24, "i7-6850K", 329.99, 2], [25, "i7-6900", 929.99, 5], [26, "i7-6950X", 1649.99, 3]] stop_sgn = "" print("[", end="") for val_list in values: stop_sgn += "{" for key, val in zip(keys, val_list): if type(val) is str: print("{}\"{}\": \"{}\"".format(stop_sgn, key, val), end="") else: print("{}\"{}\": {}".format(stop_sgn, key, val), end="") stop_sgn = ", " print("}") stop_sgn = "," print("]")
Zero Cool · 2017-09-29 14:33
>>3821 Sorry for the double post but the the formatting in the previous post is bad. Let me try again: Python if __name__ == "__main__": keys = ["id", "name", "price", "qty"] values = [[23, "i7-7700", 289.99, 5], [24, "i7-6850K", 329.99, 2], [25, "i7-6900", 929.99, 5], [26, "i7-6950X", 1649.99, 3]] stop_sgn = "" print("[", end="") for val_list in values: stop_sgn += "{" for key, val in zip(keys, val_list): if type(val) is str: print("{}\"{}\": \"{}\"".format(stop_sgn, key, val), end="") else: print("{}\"{}\": {}".format(stop_sgn, key, val), end="") stop_sgn = ", " print("}") stop_sgn = "," print("]")
Unspecified · 2017-09-29 18:33
>>3824 FYI, you can set a password on a post to be able to delete it later. You have to enter it in the "password" field while posting, and if you want to delete that post later, you have to check the little rectangle in the upper-left corner and type it in again in the field that would appear below. The safest way to do this is probably to generate a random password each time and keep it in a text editor or something similar, without saving, only as long as it takes you to examine the post and confirm that there is indeed nothing wrong with it. Also, don't use tabs when posting code. I'll provide a solution in my homebrew Lisp so that this post is not entirely off topic. This version doesn't print anything, just zips the values and keys together like OP described in their later posts: (let ((keys '("id" "name" "price" "qt")) (values '((23 "i7-7700" 289.99 5) (24 "i7-6850K" 329.99 2) (25 "i7-6900" 929.99 5) (26 "i7-6950X" 1649.99 3)))) (for (zip value keys) ((value values))))
Haruyoshi · 2017-09-29 23:02
>>3821 >>3824 >Still learing python - Instead of dumping your code directly under "__main__", put it in a main() method. That allows optional post-import execution in the interactive interpreter. - The formatting/printing code belongs in its own method with keys and values as parameters. - You can put a string in single quotes to avoid having to escape double quotes. - The inner loop translates to a string and adds separators. This is a job for the map built-in and the join method of str. The outer loop is another map and join. Once you get rid of both loops you are on your way to no longer writing C in Python.
x<->y · 2017-10-02 19:38
>>3838 Thanks for the tips. I will try to rethink my standard approach of writing code in python next time.
Gyula Kovacs · 2017-10-02 22:18
Java...not the best solution A situation where the data is just string arrays. String[][] data = { {"23", "\"i7-7700\"", "289.99", "5"}, {"24", "\"i7-6850K\"", "329.99", "2"}, {"25", "\"i7-6900\"", "929.99", "5"}, {"26", "\"i7-6950X\"", "1649.99", "3"} }; String[] keys = {"id", "name", "price", "qty"}; System.out.print("{");//open main object for(int row = 0; row < data.length; row++) {//loop through each row System.out.print("{");//open json sub object for(int col = 0; col < data.length; col++) { System.out.print("\"" + keys[col] + "\": " + data[row][col] + (col < data.length - 1 ? ", " : ""));//data } System.out.print("}" + (row < data.length - 1 ? "," : "}"));//close json sub object, comma if not the last row, close object main object System.out.println();//print the whole line } Output: [code] {{"id": 23, "name": "i7-7700", "price": 289.99, "qty": 5}, {"id": 24, "name": "i7-6850K", "price": 329.99, "qty": 2}, {"id": 25, "name": "i7-6900", "price": 929.99, "qty": 5}, {"id": 26, "name": "i7-6950X", "price": 1649.99, "qty": 3}} [code]
Donna Hawthorne · 2017-10-03 17:20
>>3994 Use the interpreter as a REPL to debug individual functions while writing your code. That's pretty much how I got into the habit. An editor that helps such a pattern like emacs may be helpful. Once you get used to it, it gets annoying not to have it. For example, keep the REPL open, load some network information through an IO-only function (separate IO and impure functions from pure ones as much as you can, it helps this type of debugging) into a variable, debug your pure functions anywhere regardless of network connectivity. Or just be friendly and don't make hundreds of queries to a server when the network/IO part isn't the one that's failing.