www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Calculating/Averaging over a struct value

reply Brian Brady <brian.brady1982 gmail.com> writes:
All

This might be relatively trivial so please point me at documentation to read
if it is.
I am creating an array of Structs(is this the best thing to do) as per the
example below.

#! /usr/bin/rdmd

import std.array;
import std.csv;
import std.stdio;
import std.string;

struct Data
{
  string Date;
  double d1;
  double d2;
  int i4;
}

double average(Data[] x, string para)
{
  if (para != "d1")
    return -1;
  else
  {
// Here I want to dynamically average over whatever was entered as para
// but if I write x[].para if throws an error
    foreach(i;0 .. x[].length)
    writeln("bob");
    return 1;
  }
}

void main()
{
  Data[] allData;
  string text= "2011-11-30,2.25,3.00,100\n2011-11-29,2.24,2.75,1000\r\n";

  foreach(ob;csvReader!Data(text))
  {
    allData ~= ob;
  }

  writeln(allData);

  string input;
  writeln("Enter the variable you would like to average over: ");
  stdin.readln(input);

  writeln(input);
}

As per the commented section, I want to be able to dynamically figure out,
which member of the struct to average across, for all the structs in the array.

Is there any way of doing this, or will I have to code one for each possibility.
ie.
  if(para = "d1")
    ... calculate the average of x[].d1
  else if(para = "d2")
    ... calculate the average of x[].d2

Is there a better method of doing this that I can look at?

Thanks in advance.
Brian
Mar 21 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
#! /usr/bin/rdmd

import std.array;
import std.csv;
import std.stdio;
import std.string;

struct Data{
     string Date;
     double d1;
     double d2;
     int i4;
}

double average(Data[] x, string para){
     double result = 0.0;
     theswitch:switch(strip(para)){
             foreach(member;__traits(allMembers, Data)){
                 case member:
                     static if(__traits(compiles, 
{result+=mixin(`x[0].`~member);})){
                         foreach(d; x){
                             result+=mixin(`d.`~member);
                         }
                     }else{
                         throw new Exception("don't know how to average 
over "~para);
                     }
                     break theswitch;
         }
         default: throw new Exception("no member named "~para);
     }

     return result/x.length;
}

void main(){
     Data[] allData;
     string text= "2011-11-30,2.25,3.00,100\n2011-11-29,2.24,2.75,1000\r\n";

     foreach(ob;csvReader!Data(text)){
             allData ~= ob;
     }

     writeln(allData);

     string input;
     writeln("Enter the variable you would like to average over: ");
     stdin.readln(input);
     try writeln(average(allData,input));
     catch(Exception e){writeln(e.msg);}
}
Mar 21 2012
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Brian Brady:

 // but if I write x[].para if throws an error

This is a quite natural syntax, and I think it's handy, but it's not supported yet. Bye, bearophile
Mar 21 2012
parent Brian Brady <brian.brady1982 gmail.com> writes:
Thanks for the replies.

Timons reply answers my question ... now I just have to figure out how :P
Mar 21 2012
prev sibling next sibling parent "David Nadlinger" <see klickverbot.at> writes:
On Wednesday, 21 March 2012 at 18:11:11 UTC, bearophile wrote:
 Brian Brady:

 // but if I write x[].para if throws an error

This is a quite natural syntax, and I think it's handy, but it's not supported yet.

I don't think this will ever be supported (without using opDispatch) – para is a runtime value here… David
Mar 21 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
David Nadlinger:

 I don't think this will ever be supported (without using 
 opDispatch) – para is a runtime value here…

 David

Right, right, sorry, I meant a compile time argument. And even that, it's not clear what arr[].foo means. Maybe a lazy Range of that field, that is a lazy column. Bye, bearophile
Mar 21 2012
prev sibling next sibling parent "Jesse Phillips" <Jessekphillips+D gmail.com> writes:
On Wednesday, 21 March 2012 at 17:02:05 UTC, Brian Brady wrote:

 As per the commented section, I want to be able to dynamically 
 figure out,
 which member of the struct to average across, for all the 
 structs in the array.

Timon has given you an good example to get D to generate some code for you. I would also point out that combining your if block or even Timon's solution with a template: double average(string param)(Data[] x) { static if (!isNumeric!(typeof(mixin("Data."~param)))) return -1; else { //auto average = reduce!("a."~param~" + b."~param)(x)/x.length; typeof(return) average = 0; foreach(v; x) { mixin("average += v."~param~";"); } return average/x.length; } } Note, I commented out reduce as it uses the array type instead of the calculation type. I think I'll file than as a bug. Also the reduce version would not give you a double back even if it did work, need a cast in there. Now calculating the parameter is just auto ans = average!"d1"(x);
Mar 21 2012
prev sibling parent "Jesse Phillips" <jessekphillips+D gmail.com> writes:
On Wednesday, 21 March 2012 at 20:13:37 UTC, Jesse Phillips wrote:

 Note, I commented out reduce as it uses the array type instead 
 of the calculation type. I think I'll file than as a bug. Also 
 the reduce version would not give you a double back even if it 
 did work, need a cast in there.

Nevermind, just needed to map double average(string param)(Data[] x) { static if (!isNumeric!(typeof(mixin("Data."~param)))) return -1; else { return cast(double)reduce!("a + b")(map!("a."~param)(x))/x.length; } }
Mar 21 2012