<div dir="ltr">Andrew, if you want to store the data in a format that is as compact as possible, I'd recommend storing the HL7 message itself as a binary and parsing on demand. If you want to store the data pre-parsed, then I would store them as list of segments where each segment is represented by a nested tuple. That way you can reference the fields, components, etc., by their index in an O(1) operation, and you can still easily add or remove segments from a message.<br><br>What I'm describing is similar to the intermediate format used by an HL7 parser (<a href="https://github.com/jcomellas/ex_hl7">https://github.com/jcomellas/ex_hl7</a>) I wrote for Elixir. You could probably use it as inspiration for what you need. I had also created another parser in Erlang (<a href="https://github.com/jcomellas/ehl7">https://github.com/jcomellas/ehl7</a>) that maps the segments to records, but part of it is in C using NIFs.<div><br></div><div>Let me know if you have any other doubts.</div><div><br></div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Aug 7, 2017 at 10:46 AM, Andrew McIntyre <span dir="ltr"><<a href="mailto:andrew@medical-objects.com.au" target="_blank">andrew@medical-objects.com.au</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hello Craig,<br>
<br>
Thanks for your help.<br>
<br>
I am trying to store the data as efficiently as possible. Its HL7<br>
natively and this is my test:<br>
<br>
OBX|17|FT~TEST|8265-1^^LN&<wbr>SUBCOMP|1&2&3&4|\H\Spot Image 2\N\||||||F<br>
<br>
|~^& are delimiters. The hierarchy is only so deep and using lists of<br>
lists to provide a tree like way to access the data eg Field 3, repeat<br>
1 component 2 subcomponent1<br>
<br>
Parsed it looks like this:<br>
<br>
[["OBX","17",<br>
  ["FT","TEST"],<br>
  [["8265-1",[],["LN","SUBCOMP"]<wbr>]],<br>
  [[["1","2","3","4"]]],<br>
  "\\H\\Spot Image 2\\N\\",[],[],[],[],[],"F"]]<br>
<br>
As the format evolves over time the hierarchy can be extended, but<br>
older clients can still read the value they are expecting if they<br>
follow the rules, like reading the first value in the list when you<br>
only expect one value to be there.<br>
<br>
Currently a typical system might have 12 million of these records so<br>
want to keep format as small as possible in the erlang format, hence<br>
reluctant to tag 2 much, but know how to get value of interest. Maybe<br>
that is my non erlang background showing up? Traversing 4 small lists<br>
by index should be fast??<br>
<br>
I guess I could save strings as binary in the lists then is_binary<br>
should work?? Is that the case. I gather on 64bit system especially<br>
binary is more space efficient.<br>
<br>
Monday, August 7, 2017, 10:53:11 PM, you wrote:<br>
<span class=""><br>
z> On 2017年08月07日 月曜日 22:29:31 you wrote:<br>
>> Hello zxq9,<br>
>><br>
>> Thanks, Unfortunately I do not know the value of the string that will<br>
>> be there. Its an extensible hierarchy that can be several lists deep -<br>
>> or not. Might need to revise the data structure<br>
<br>
</span>z> In this case it can be useful to consider a way of tagging values.<br>
<br>
z> Imagine we want to represent a directory tree structure and have a<br>
z> descent-first traversal function recurse over it while creating the<br>
z> tree. We have two things that can happen, there is a flat list of<br>
z> new directories that need to be created, and there is the<br>
z> possibility that the tree depth extends deeper at each node.<br>
<br>
z> The naive version would look like what you have:<br>
<br>
z> ["top_dir_1",<br>
z>  "top_dir_2",<br>
z>  ["next_level_1",<br>
z>   "next_level_2"]]<br>
<br>
z> This leaves a bit to be desired, not only because of the problem<br>
z> you have pointed out that makes it difficult to know what is deep<br>
z> and what is shallow, but also because we don't really have a good<br>
z> way to represent a full tree (what would be the name of a directory containing other directories?).<br>
<br>
z> So consider instead something like this:<br>
<br>
z> [{"top_dir_1", []},<br>
z>  {"top_dir_2", []},<br>
z>  {"top_dir_3",<br>
z>   [{"next_level_1", []},<br>
z>    {"next_level_2", []}]}]<br>
<br>
z> Now we have a representation of each directory's name AND its contents.<br>
<br>
z> We can traverse this laterally AND in depth without any ambiguity<br>
z> or need for carrying around a record of where we have been (by<br>
z> using depth recursion and tail-call recursion):<br>
<br>
<br>
z> make_tree([{Dir, Contents} | Rest]) -><br>
z>     ok =<br>
z>         case filelib:is_dir(Dir) of<br>
z>             true -><br>
z>                 ok;<br>
z>             false -><br>
z>                 ok = log(info, "Creating dir: ~p", [Dir]),<br>
z>                 file:make_dir(Dir)<br>
z>         end,<br>
z>     ok = file:set_cwd(Dir),<br>
z>     ok = make_tree(Contents),<br>
z>     ok = file:set_cwd(".."),<br>
z>     make_tree(Rest);<br>
make_tree([]) ->><br>
z>     ok.<br>
<br>
<br>
z> Not so bad.<br>
<br>
z> In your case we could represent things perhaps a bit better by<br>
z> separating the types and tagging them. Instead of just "FT" and<br>
z> whatever other string labels you might want, you could either use<br>
z> atoms (totally unambiguous) or tuples as we have in the example<br>
z> able (also totally unambiguous). I prefer tuples, though, because they are easier to read.<br>
<br>
z> [{value, "foo"},<br>
z>  {tree,<br>
z>   [{value, "bar"},<br>
z>    {value, "foo"}]},<br>
z>  {value, "baz"}]<br>
<br>
<br>
z> So then we do something like:<br>
<br>
<br>
z> traverse([{value, Value} | Rest]) -><br>
z>    ok = do_thing(Value),<br>
z>    traverse(Rest);<br>
z> traverse([{tree, Contents} | Rest]) -><br>
z>    ok = traverse(Contents),<br>
z>    traverse(Rest);<br>
traverse([]) ->><br>
z>    ok.<br>
<br>
<br>
z> Anyway, don't be afraid of varying your value types to say exactly<br>
z> what you mean. If your strings like "FT" only had meaning within<br>
z> your system consider NOT USING STRINGS, and using atoms instead. That makes it even easier:<br>
<br>
<br>
z> [foo,<br>
z>  bar,<br>
z>  [foo,<br>
z>   bar],<br>
z>  foo]<br>
<br>
<br>
z> So then we can do:<br>
<br>
<br>
z> traverse([foo | Rest]) -><br>
z>     ok = do_foo(),<br>
z>     traverse(Rest);<br>
z> traverse([bar | Rest]) -><br>
z>     ok = do_bar(),<br>
z>     traverse(Rest);<br>
z> traverse([Value | Rest]) when is_list(Value) -><br>
z>     ok = traverse(Value),<br>
z>     traverse(Rest);<br>
traverse([]) ->><br>
z>     ok.<br>
<br>
<br>
z> And of course, you can not use a guard if you want to match on a<br>
z> list shape in the listy clause there, but that is a minor detail.<br>
z> The point is to make your data types MEAN SOMETHING REASONABLE<br>
z> within your system. Use atoms when your values are meaningful only<br>
z> within your system. Strings are for the birds.<br>
<br>
z> -Craig<br>
z> ______________________________<wbr>_________________<br>
z> erlang-questions mailing list<br>
z> <a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
z> <a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" target="_blank">http://erlang.org/mailman/<wbr>listinfo/erlang-questions</a><br>
<span class="HOEnZb"><font color="#888888"><br>
<br>
<br>
--<br>
Best regards,<br>
 Andrew                             mailto:<a href="mailto:andrew@Medical-Objects.com.au">andrew@Medical-Objects.<wbr>com.au</a><br>
</font></span><span class="im HOEnZb"><br>
sent from a real computer<br>
<br>
<br>
</span><div class="HOEnZb"><div class="h5">______________________________<wbr>_________________<br>
erlang-questions mailing list<br>
<a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
<a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" target="_blank">http://erlang.org/mailman/<wbr>listinfo/erlang-questions</a><br>
</div></div></blockquote></div><br></div>