I have CSV data (inherited - no choice here) which I need to use to create data type instances in Haskell. parsing CSV is no problem - tutorials and APIs abound.
Here's what 'show' generates for my simplified trimmed-down test-case:
JField {fname = "cardNo", ftype = "str"} (string representation)
I am able to do a read to convert this string into a JField data record. My CSV data is just the values of the fields, so the CSV row corresponding to JField above is:
cardNo, str
and I am reading these in as List of string ["cardNo", "str"]
So - it's easy enough to brute-force the exact format of "string representation" (but writing Java or python-style string-formatting in Haskell isn't my goal here).
I thought of doing something like this (the first List is static, and the second list would be read file CSV) :
let stp1 = zip ["fname = ", "ftype ="] ["cardNo", "str"]
resulting in
[("fname = ","cardNo"),("ftype =","str")]
and then concatenating the tuples - either explicitly with ++ or in some more clever way yet to be determined.
This is my first simple piece of code outside of tutorials, so I'd like to know if this seems a reasonably Haskellian way of doing this, or what clearly better ways there are to build just this piece:
fname = "cardNo", ftype = "str"
Not expecting solutions (this is not homework, it's a learning exercise), but rather critique or guidelines for better ways to do this. Brute-forcing it would be easy but would defeat my objective, which is to learn
I might be way off, but wouldn't a map be better here? I guess I'm assuming that you read the file in with each row as a [String] i.e.
field11, field12
field21, field22
etc.
You could write
map (\[x,y] -> JField {fname = x, ftype = y}) data
where data is your input. I think that would do it.
If you already have the value of the fname field (say, in the variable fn) and the value of the ftype field (in ft), just do JField {fname=fn, ftype=ft}. For non-String fields, just insert a read where appropriate.
Related
I'm trying to get some .txt files , reading them in order and then writing their content( with some logic) into an excel sheet.
The files are named like this:
FileB_60.txt
FileB_90.txt
FileB_120.txt
fileA_60.txt
fileA_90.txt
fileA_120.txt
In the end, I want them to be read just like that, ordered by name and number. If I don't use any sorting step, they're read randomly.
String fileExtension = ".txt";
try (Stream<Path> paths = Files.walk(Paths.get(resourcesPath))) {
Comparator<Path> byName = Comparator.comparing(p -> p.getFileName().toString());
Comparator<Path> byNameAndNumber = byName.thenComparingInt(p -> Integer.parseInt(p.getFileName().toString().split("_")[1].replace(fileExtension, "")));
for (Path eachFilePath : paths
.filter(p -> p.getFileName().toString().endsWith(fileExtension))
.sorted(byNameAndNumber)
.collect(Collectors.toList()))
...
This way, they're read like:
FileB_120.txt
FileB_60.txt
FileB_90.txt
fileA_120.txt
fileA_60.txt
fileA_90.txt
So it seems like it's ordering only by the name (capital letter first)
I was wondering if I'm doing something wrong here, since I'm sorting within the same string
One important thing about thenComparing... methods: the comparator injected is only used when the initial comparator (called byName in your example) considers the 2 elements are equal, which is never the case in your example.
The comparator byName must compare only the first part of the file name (the one before the _).
Comparator<Path> byName = Comparator.comparing(
p -> p.getFileName().toString().split("_")[0]);
I need to update one field of a very large default record.
As the default may change I don't want to rebuild the entire record manually.
Now I have come across the following way of doing this, but I am not sure how it works:
unaggregate :: MyResult -> MyResult
unaggregate calc#MyResult{..} = calc{ the_defaults = the_override
`mappend` the_defaults }
where
the_override = create ("aggregation" := False)
I have tried searching for 'Haskell # operator' in Google but it does not return immediately useful information.
I saw somewhere calc#MyResult{..} does pattern matching on variables but I don't see what variable calc does for the MyResult record...
Also I have looked up mappend (and Monoids) and I am not sure how these work either...
Thank you for any help
The # symbol is called an "as-pattern". In the example above, you can use calc to mean the whole record. Usually you'd use it like this: calc#(MyResult someResult) -- so you can have both the whole thing and the pieces that you're matching. You can do the same thing with lists (myList#(myHead:myTail)) or tuples (myTuple#(myFst, mySnd). It's pretty handy!
MyResult{..} uses RecordWildcards. Which is a neat extension! BUT RecordWildcards doesn't help you update just one field of a record.
You can do this instead: calc { theFieldYouWantToUpdate = somethingNew }.
I am attempting to write an algorithm that selects a specific reference standard (vector) as a function of temperature. The temperature values are stored in a structure ( procspectra(i).temperature ). My reference standards are stored in another structure ( standards.interp.zeroed.ClOxxx ) where xxx are numbers such as 200, 210, 220, etc. I have built the rounding construct and paste it below.
for i = 1:length(procspectra);
if mod(-procspectra(i).temperature,10) > mod(procspectra(i).temperature,10);
%if mod(-) > mod(+) round down, else round up
tempvector(i) = procspectra(i).temperature - mod(procspectra(i).temperature,10);
else
tempvector(i) = procspectra(i).temperature + mod(-procspectra(i).temperature,10);
end
clostd = strcat('standards.interp.zeroed.ClO',num2str(tempvector(i)));
end
This construct works well. Now, I have built a string which is identical to the name of the vector I want to invoke, but I'm uncertain how to actually call the vector given that this is encoded as a string. Ideally I want to do something within the for-loop like:
parameters(i).standards.ClOstandard = clostd
where I actually am assigning that parameter structure to be the same as the vector I have saved in the standards structure I have previously generated (and not just a string)
Could anyone help out?
Don't construct clostd like that (containing the full variable name), make it contain only the last field name instead:
clostd = ['ClO' num2str(tempvector(i))];
parameters(i).standards.ClOstandard = standards.interp.zeroed.(clostd);
This is the syntax of accessing a structure's field dynamically, using a string. So the following three are equivalent:
struc.Cl0123
struc.('Cl0123')
fieldn='Cl0123'; struc.(fieldn)
I have a string and want to check if in the workspace exist any variable with the same name. In the workspace I have also many structures M.N.O M.N.N M.N.M etc. I can only check if there exist a variable with the name M. How to go deeper into this structure?
I tried:
exist('M.N')
YesNo = any(strcmp(who,'M.N.O'))
evalin('base','exist(''M.N.O'',''var'')')
all give me the same problem so I am stuck.
You can use isfield to check if a variable has a specific field. See the link for examples!
For your example, you'd need:
isfield(M,'N')
and if true, you can go deeper:
isfield(M.N,'O')
Notice that
isfield(M,'N.O')
won't work ;)
One option: write a recursive function to expand structures down to their leaf fields, appending the fields to a list.
(untested, conceptual code - probably won't work quite right as is)
function varlist = getStructFields(var,varlist)
if isstruct(var)
fn = fieldnames(var);
varlist = vertcat(varlist,fn); %# append fields to the list
for field = fn' %# ' create row vector; iterate through fields
varlist = getStructFields(var.(char(field)), varlist); %# recursion here
end
end
end
Then you can use the any(strcmp(who,'M.N.O')) check you already came up with.
I am trying to create a file with a name based on an integer value from a function, clearly below does not work but gives you the idea :
getValue() -> 1.
createFile() ->
{ok, File} = file:open( getValue(), [write]),
io:format(File,"Test~n"),
file:close(File).
This ought to be simple, even with Erlangs lack of support for strings, so I must just be missing something obvious ( as is the price of being new to something ) :
If you just want to open a file whose name is "1", then you can use integer_to_list/1 to do that (since a string is just a list of integers for the ASCII values of the characters):
getValue() -> 1.
....
{ok, File} = file:open(integer_to_list(getValue()), [write]),
If you're wanting to create a filename based on the value from getValue/0, then the same principle applies, but you just create your filename from gluing several lists together.