{"action":"created","comment":{"url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/comments/24865127","id":24865127,"diff_hunk":"@@ -0,0 +1,214 @@\n+module Data.Json.JTable.Internal\n+  ( renderJTableRaw, renderRows, renderThead, renderTbody, tsToRows, sortTree\n+  , Tree(..), tPath, tWidth, tHeight, tKids\n+  , Cell(..), cCursor, cWidth, cHeight, cJsonPrim\n+  , JPath(..), Table(..)\n+  , tFromJson, tMergeArray, widthOfPrimTuple, insertHeaderCells\n+  , cFromJson, cMergeObj, mergeObjTuple\n+  , _cN, toPrim, strcmp, _nattr, _cspan, _rspan\n+  ) where\n+\n+import Math (max)\n+import Data.Either\n+import Data.Maybe (Maybe(..), fromMaybe, isJust)\n+import Data.Tuple\n+import Data.String (joinWith, localeCompare)\n+import Data.Array\n+import Data.Foldable\n+import qualified Data.Array.Unsafe as AU\n+import qualified Data.StrMap as M\n+import Data.Foldable (foldl, any, all, mconcat, elem)\n+import Data.Traversable (for)\n+import Control.Alt\n+import Control.Apply\n+import Control.Bind\n+import Control.Monad\n+import Control.MonadPlus\n+\n+import Data.Argonaut.Core\n+import Data.Argonaut.JCursor\n+import Data.Argonaut.Parser (jsonParser)\n+import Text.Smolder.HTML (thead, tbody, tr, th, td)\n+import Text.Smolder.Markup (Markup(..), Attribute(..), attribute, (!))\n+\n+-- path of object keys, with array indices omitted\n+type JPath = [String]\n+\n+-- rows of cells\n+type Table = [[Cell]]\n+\n+-- header data\n+data Tree = T JPath Number Number [Tree]\n+tPath (T p _ _ _) = p","path":"src/Data.Json.JTable.Internal.purs","position":42,"original_position":42,"commit_id":"429adef13ef36f667f7a7c2f4bd5fa6264193ae0","original_commit_id":"429adef13ef36f667f7a7c2f4bd5fa6264193ae0","user":{"login":"jdegoes","id":156745,"avatar_url":"https://avatars.githubusercontent.com/u/156745?v=3","gravatar_id":"","url":"https://api.github.com/users/jdegoes","html_url":"https://github.com/jdegoes","followers_url":"https://api.github.com/users/jdegoes/followers","following_url":"https://api.github.com/users/jdegoes/following{/other_user}","gists_url":"https://api.github.com/users/jdegoes/gists{/gist_id}","starred_url":"https://api.github.com/users/jdegoes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jdegoes/subscriptions","organizations_url":"https://api.github.com/users/jdegoes/orgs","repos_url":"https://api.github.com/users/jdegoes/repos","events_url":"https://api.github.com/users/jdegoes/events{/privacy}","received_events_url":"https://api.github.com/users/jdegoes/received_events","type":"User","site_admin":false},"body":"This is totally fine for this PR, although you may be interested in checking out purescript-lens for future projects. They make short work of getting and \"setting\" members of a data value.","created_at":"2015-02-17T23:19:51Z","updated_at":"2015-02-17T23:19:51Z","html_url":"https://github.com/slamdata/purescript-jtable/pull/11#discussion_r24865127","pull_request_url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11","_links":{"self":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/comments/24865127"},"html":{"href":"https://github.com/slamdata/purescript-jtable/pull/11#discussion_r24865127"},"pull_request":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11"}}},"pull_request":{"url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11","id":29364385,"html_url":"https://github.com/slamdata/purescript-jtable/pull/11","diff_url":"https://github.com/slamdata/purescript-jtable/pull/11.diff","patch_url":"https://github.com/slamdata/purescript-jtable/pull/11.patch","issue_url":"https://api.github.com/repos/slamdata/purescript-jtable/issues/11","number":11,"state":"open","locked":false,"title":"implement purescript-jtable","user":{"login":"brainrape","id":302429,"avatar_url":"https://avatars.githubusercontent.com/u/302429?v=3","gravatar_id":"","url":"https://api.github.com/users/brainrape","html_url":"https://github.com/brainrape","followers_url":"https://api.github.com/users/brainrape/followers","following_url":"https://api.github.com/users/brainrape/following{/other_user}","gists_url":"https://api.github.com/users/brainrape/gists{/gist_id}","starred_url":"https://api.github.com/users/brainrape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brainrape/subscriptions","organizations_url":"https://api.github.com/users/brainrape/orgs","repos_url":"https://api.github.com/users/brainrape/repos","events_url":"https://api.github.com/users/brainrape/events{/privacy}","received_events_url":"https://api.github.com/users/brainrape/received_events","type":"User","site_admin":false},"body":"Here's my implementation of purescript-jtable.\r\nIt has all the high-level features in the requirements. Some things are implemented differently though, and I hope you will accept my reasoning below.\r\n\r\nI recommend to [Try it](http://rawgit.com/brainrape/purescript-jtable/dev-squash/examples/try.html) with different renderers and  example data (including the \"Comprehensive\" example) right away, look at [example usage](https://github.com/brainrape/purescript-jtable/blob/dev-squash/src/Data.Json.JTable.Examples.purs) and then read the analysis below.\r\n\r\n### Types, header\r\n\r\nThe label hierarchy is unified based on its shape. It becomes a hierarchy of nodes with width. Primitives have width 1, tuples more. The concrete types of the primitives are irrelevant, only the width is used. Arrays are not represented, they are unified into the most general shape hierarchy. Thus it is ensured that all data present in the Json can be rendered sensibly.\r\n\r\nContrast this to the spec, which says \"Classify every leaf node using JSemantic [...], based on the most common type of semantic detected in the data\". The problems with this step include that a statistical method is suggested to determine the type of data, with no hints as to how to handle divergent leaves. Also, leaf node semantic type, and even distinguishing between primitive json types is irrelevant to the header generation, where only tuple width and label hierarchy matters. In fact, JSemantic is a different issue, independent of JTable (but which combines nicely), as discussed below.\r\n\r\n### Tuples\r\n\r\nThere were some undefined aspects of handling tuples. Here's how I chose to resolve them:\r\n\r\nHomogenous arrays of equal sizes are rendered vertically, except when their size equals 2, in which case they are rendered horizontally. This is somewhat arbitrary, but usefult for geographic coordinates.\r\n\r\nHeterogenous arrays of different sizes are rendered horizontally, with the maximum tuple size becoming the column width\r\n\r\nLists of objects with dijunct keys will be displayed in one row (treating them as tuples of objects).\r\n\r\n\r\n### TableStyle.th \r\n\r\nAnother difference is: JPath is an array of strings. Arrays (which could have different nestings) are not represented in the header hierarchy, so a [String] suffices for the path instead of the unwieldier JCursor.\r\n\r\n```purescript\r\ntype JPath = [String]\r\n```\r\n\r\nThe header rendering function gets a JPath as an argument.\r\n\r\n```purescript\r\nTableStyle.th :: JPath -> Markup\r\n```\r\n\r\nThe default implementation is equivalent to\r\n```purescript\r\nnoStyle.th = \\p -> th $ text $ Data.Array.Unsafe.last p\r\n```\r\n\r\nWhere `th` and `text` are from Smolder and the path is guaranteed to have at least one element.\r\n\r\n### Column Ordering\r\n\r\nThe `ColumnOrdering` type has been simplified, `inOrdering` is a value. Easy to roll your own.\r\n\r\n```\r\ntype ColumnOrdering = JPath -> JPath -> Ordering \r\ninOrdering    :: ColumnOrdering\r\nalphaOrdering :: ColumnOrdering\r\n```\r\n\r\n### TableStyle.td\r\n\r\nJTable does not depend on JSemantic. In fact, I recommend releasing JSemantic as a separate library, since it is orthogonal to JTable and separate from Argonaut (although it uses the JsonPrim type). However, for now I have inluded it here.\r\n\r\nAccordingly, `td` receives a JsonPrim argument that is easy to use.\r\n\r\n```purescript\r\nTableStyle.td :: JCursor -> JsonPrim -> Markup\r\n```\r\n\r\n### JSemantic\r\n\r\nData constructors of the type JSemantic have parameters and carry their data with them, converted to a useful type.\r\n\r\n```purescript\r\ndata JSemantic = Integral   Number\r\n               | Fractional Number\r\n               | Date       Date.Date\r\n               | DateTime   Date.Date\r\n               | Time       Date.Date\r\n               | Interval   Date.Date Date.Date\r\n               | Text       String\r\n               | Bool       Boolean\r\n               | Percent    Number\r\n               | Currency   Number\r\n               | NA\r\n```\r\nAlthough String might be a better choice for Currency and Percent, to avoid loss of precision.\r\n\r\nAnd it's easy to combine JTable and JSemantic:\r\n\r\n```purescript\r\nexampleRendererSemantic = renderJTable defJTableOpts {\r\n    style = noStyle { td = \\c j -> case toSemantic j of\r\n      Currency n -> td ! className \"money\" $ text $ show n\r\n  }}\r\n```\r\n\r\nRendering differently based on path is also easy, dispatching on the JCursor argument, using operations from Argonaut.\r\n\r\n\r\n### Also,\r\n\r\nThere are unit tests for some edge cases that I fixed along the way, and StrongCheck tests are run as well.\r\n\r\nThe code is not exceptionally fast or clean and simple, but it's not slow or too complex either. It gets the job done.\r\n\r\nI'm sure the examples, the [readme](https://github.com/brainrape/purescript-jtable/blob/dev-squash/README.md), and the above will convince you that the implementation provides a straightforward, intuitive, and correct conceptual mapping between any Json schema and the resulting table, has features equivalent to the requirements, with the differences backed by sound analysis.\r\n\r\nLet me know if changes or additions are needed.\r\n","created_at":"2015-02-16T10:56:28Z","updated_at":"2015-02-17T23:19:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d2e25aa13fa6e02863440e6d37456893eabcef34","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11/commits","review_comments_url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11/comments","review_comment_url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/comments/{number}","comments_url":"https://api.github.com/repos/slamdata/purescript-jtable/issues/11/comments","statuses_url":"https://api.github.com/repos/slamdata/purescript-jtable/statuses/429adef13ef36f667f7a7c2f4bd5fa6264193ae0","head":{"label":"brainrape:dev-squash","ref":"dev-squash","sha":"429adef13ef36f667f7a7c2f4bd5fa6264193ae0","user":{"login":"brainrape","id":302429,"avatar_url":"https://avatars.githubusercontent.com/u/302429?v=3","gravatar_id":"","url":"https://api.github.com/users/brainrape","html_url":"https://github.com/brainrape","followers_url":"https://api.github.com/users/brainrape/followers","following_url":"https://api.github.com/users/brainrape/following{/other_user}","gists_url":"https://api.github.com/users/brainrape/gists{/gist_id}","starred_url":"https://api.github.com/users/brainrape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brainrape/subscriptions","organizations_url":"https://api.github.com/users/brainrape/orgs","repos_url":"https://api.github.com/users/brainrape/repos","events_url":"https://api.github.com/users/brainrape/events{/privacy}","received_events_url":"https://api.github.com/users/brainrape/received_events","type":"User","site_admin":false},"repo":{"id":29835222,"name":"purescript-jtable","full_name":"brainrape/purescript-jtable","owner":{"login":"brainrape","id":302429,"avatar_url":"https://avatars.githubusercontent.com/u/302429?v=3","gravatar_id":"","url":"https://api.github.com/users/brainrape","html_url":"https://github.com/brainrape","followers_url":"https://api.github.com/users/brainrape/followers","following_url":"https://api.github.com/users/brainrape/following{/other_user}","gists_url":"https://api.github.com/users/brainrape/gists{/gist_id}","starred_url":"https://api.github.com/users/brainrape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brainrape/subscriptions","organizations_url":"https://api.github.com/users/brainrape/orgs","repos_url":"https://api.github.com/users/brainrape/repos","events_url":"https://api.github.com/users/brainrape/events{/privacy}","received_events_url":"https://api.github.com/users/brainrape/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/brainrape/purescript-jtable","description":"A Purescript table renderer capable of displaying multidimensional, heterogeneous JSON data","fork":true,"url":"https://api.github.com/repos/brainrape/purescript-jtable","forks_url":"https://api.github.com/repos/brainrape/purescript-jtable/forks","keys_url":"https://api.github.com/repos/brainrape/purescript-jtable/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brainrape/purescript-jtable/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brainrape/purescript-jtable/teams","hooks_url":"https://api.github.com/repos/brainrape/purescript-jtable/hooks","issue_events_url":"https://api.github.com/repos/brainrape/purescript-jtable/issues/events{/number}","events_url":"https://api.github.com/repos/brainrape/purescript-jtable/events","assignees_url":"https://api.github.com/repos/brainrape/purescript-jtable/assignees{/user}","branches_url":"https://api.github.com/repos/brainrape/purescript-jtable/branches{/branch}","tags_url":"https://api.github.com/repos/brainrape/purescript-jtable/tags","blobs_url":"https://api.github.com/repos/brainrape/purescript-jtable/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brainrape/purescript-jtable/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brainrape/purescript-jtable/git/refs{/sha}","trees_url":"https://api.github.com/repos/brainrape/purescript-jtable/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brainrape/purescript-jtable/statuses/{sha}","languages_url":"https://api.github.com/repos/brainrape/purescript-jtable/languages","stargazers_url":"https://api.github.com/repos/brainrape/purescript-jtable/stargazers","contributors_url":"https://api.github.com/repos/brainrape/purescript-jtable/contributors","subscribers_url":"https://api.github.com/repos/brainrape/purescript-jtable/subscribers","subscription_url":"https://api.github.com/repos/brainrape/purescript-jtable/subscription","commits_url":"https://api.github.com/repos/brainrape/purescript-jtable/commits{/sha}","git_commits_url":"https://api.github.com/repos/brainrape/purescript-jtable/git/commits{/sha}","comments_url":"https://api.github.com/repos/brainrape/purescript-jtable/comments{/number}","issue_comment_url":"https://api.github.com/repos/brainrape/purescript-jtable/issues/comments/{number}","contents_url":"https://api.github.com/repos/brainrape/purescript-jtable/contents/{+path}","compare_url":"https://api.github.com/repos/brainrape/purescript-jtable/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brainrape/purescript-jtable/merges","archive_url":"https://api.github.com/repos/brainrape/purescript-jtable/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brainrape/purescript-jtable/downloads","issues_url":"https://api.github.com/repos/brainrape/purescript-jtable/issues{/number}","pulls_url":"https://api.github.com/repos/brainrape/purescript-jtable/pulls{/number}","milestones_url":"https://api.github.com/repos/brainrape/purescript-jtable/milestones{/number}","notifications_url":"https://api.github.com/repos/brainrape/purescript-jtable/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brainrape/purescript-jtable/labels{/name}","releases_url":"https://api.github.com/repos/brainrape/purescript-jtable/releases{/id}","created_at":"2015-01-25T22:56:22Z","updated_at":"2015-02-02T19:03:24Z","pushed_at":"2015-02-17T18:10:57Z","git_url":"git://github.com/brainrape/purescript-jtable.git","ssh_url":"git@github.com:brainrape/purescript-jtable.git","clone_url":"https://github.com/brainrape/purescript-jtable.git","svn_url":"https://github.com/brainrape/purescript-jtable","homepage":"","size":124,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"slamdata:master","ref":"master","sha":"7dae5dfa75cbf69eb28caea12e75821cd9b7a003","user":{"login":"slamdata","id":5385602,"avatar_url":"https://avatars.githubusercontent.com/u/5385602?v=3","gravatar_id":"","url":"https://api.github.com/users/slamdata","html_url":"https://github.com/slamdata","followers_url":"https://api.github.com/users/slamdata/followers","following_url":"https://api.github.com/users/slamdata/following{/other_user}","gists_url":"https://api.github.com/users/slamdata/gists{/gist_id}","starred_url":"https://api.github.com/users/slamdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slamdata/subscriptions","organizations_url":"https://api.github.com/users/slamdata/orgs","repos_url":"https://api.github.com/users/slamdata/repos","events_url":"https://api.github.com/users/slamdata/events{/privacy}","received_events_url":"https://api.github.com/users/slamdata/received_events","type":"Organization","site_admin":false},"repo":{"id":29360348,"name":"purescript-jtable","full_name":"slamdata/purescript-jtable","owner":{"login":"slamdata","id":5385602,"avatar_url":"https://avatars.githubusercontent.com/u/5385602?v=3","gravatar_id":"","url":"https://api.github.com/users/slamdata","html_url":"https://github.com/slamdata","followers_url":"https://api.github.com/users/slamdata/followers","following_url":"https://api.github.com/users/slamdata/following{/other_user}","gists_url":"https://api.github.com/users/slamdata/gists{/gist_id}","starred_url":"https://api.github.com/users/slamdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slamdata/subscriptions","organizations_url":"https://api.github.com/users/slamdata/orgs","repos_url":"https://api.github.com/users/slamdata/repos","events_url":"https://api.github.com/users/slamdata/events{/privacy}","received_events_url":"https://api.github.com/users/slamdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/slamdata/purescript-jtable","description":"A Purescript table renderer capable of displaying multidimensional, heterogeneous JSON data","fork":false,"url":"https://api.github.com/repos/slamdata/purescript-jtable","forks_url":"https://api.github.com/repos/slamdata/purescript-jtable/forks","keys_url":"https://api.github.com/repos/slamdata/purescript-jtable/keys{/key_id}","collaborators_url":"https://api.github.com/repos/slamdata/purescript-jtable/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/slamdata/purescript-jtable/teams","hooks_url":"https://api.github.com/repos/slamdata/purescript-jtable/hooks","issue_events_url":"https://api.github.com/repos/slamdata/purescript-jtable/issues/events{/number}","events_url":"https://api.github.com/repos/slamdata/purescript-jtable/events","assignees_url":"https://api.github.com/repos/slamdata/purescript-jtable/assignees{/user}","branches_url":"https://api.github.com/repos/slamdata/purescript-jtable/branches{/branch}","tags_url":"https://api.github.com/repos/slamdata/purescript-jtable/tags","blobs_url":"https://api.github.com/repos/slamdata/purescript-jtable/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/slamdata/purescript-jtable/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/slamdata/purescript-jtable/git/refs{/sha}","trees_url":"https://api.github.com/repos/slamdata/purescript-jtable/git/trees{/sha}","statuses_url":"https://api.github.com/repos/slamdata/purescript-jtable/statuses/{sha}","languages_url":"https://api.github.com/repos/slamdata/purescript-jtable/languages","stargazers_url":"https://api.github.com/repos/slamdata/purescript-jtable/stargazers","contributors_url":"https://api.github.com/repos/slamdata/purescript-jtable/contributors","subscribers_url":"https://api.github.com/repos/slamdata/purescript-jtable/subscribers","subscription_url":"https://api.github.com/repos/slamdata/purescript-jtable/subscription","commits_url":"https://api.github.com/repos/slamdata/purescript-jtable/commits{/sha}","git_commits_url":"https://api.github.com/repos/slamdata/purescript-jtable/git/commits{/sha}","comments_url":"https://api.github.com/repos/slamdata/purescript-jtable/comments{/number}","issue_comment_url":"https://api.github.com/repos/slamdata/purescript-jtable/issues/comments/{number}","contents_url":"https://api.github.com/repos/slamdata/purescript-jtable/contents/{+path}","compare_url":"https://api.github.com/repos/slamdata/purescript-jtable/compare/{base}...{head}","merges_url":"https://api.github.com/repos/slamdata/purescript-jtable/merges","archive_url":"https://api.github.com/repos/slamdata/purescript-jtable/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/slamdata/purescript-jtable/downloads","issues_url":"https://api.github.com/repos/slamdata/purescript-jtable/issues{/number}","pulls_url":"https://api.github.com/repos/slamdata/purescript-jtable/pulls{/number}","milestones_url":"https://api.github.com/repos/slamdata/purescript-jtable/milestones{/number}","notifications_url":"https://api.github.com/repos/slamdata/purescript-jtable/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/slamdata/purescript-jtable/labels{/name}","releases_url":"https://api.github.com/repos/slamdata/purescript-jtable/releases{/id}","created_at":"2015-01-16T17:51:42Z","updated_at":"2015-02-14T04:13:20Z","pushed_at":"2015-02-02T16:41:09Z","git_url":"git://github.com/slamdata/purescript-jtable.git","ssh_url":"git@github.com:slamdata/purescript-jtable.git","clone_url":"https://github.com/slamdata/purescript-jtable.git","svn_url":"https://github.com/slamdata/purescript-jtable","homepage":"","size":232,"stargazers_count":4,"watchers_count":4,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":4,"forks":3,"open_issues":4,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11"},"html":{"href":"https://github.com/slamdata/purescript-jtable/pull/11"},"issue":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/issues/11"},"comments":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/issues/11/comments"},"review_comments":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11/comments"},"review_comment":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/pulls/11/commits"},"statuses":{"href":"https://api.github.com/repos/slamdata/purescript-jtable/statuses/429adef13ef36f667f7a7c2f4bd5fa6264193ae0"}}}}