1

I'm trying to define a mapping, where each value should be an array of struct. Not sure, if this is possible? If yes then what wrong I'm doing here?

After defining, I also need to push values to the array as shown below. Please advise if it is correct.

struct Review {
    uint rating;
    address reviewer;
    string description;
    uint dateAdded;
}


mapping (uint => Review[]) public ratings;

function rateBook(uint id, uint rating, string comments) onlyMember {
    if (id > numBooks || rating < 1 || rating > 5) {
        throw;
    }

    ratings[id].push(Review({
        rating: rating,
        reviewer: msg.sender,
        description: comments,
        dateAdded: now
        }));
}
1

Yes this is correct, here's my test code:

import "dapple/test.sol";

contract MappingArrayOfStructs is Test {

  struct Review {
    uint rating;
    address reviewer;
    string description;
    uint dateAdded;
  }

  mapping (uint => Review[]) ratings;

  function testMappingArrayOfStructs() {
    //@log push to ratings array
    ratings[0].push(Review({
      rating: 0,
      reviewer: msg.sender,
      description: "",
      dateAdded: now
    }));
    //@log `uint ratings[0][0].dateAdded`
  }

}

This can be run with dapple test --report and produces the following report:

MappingArrayOfStructs
  test mapping array of structs
  LOG:  push to ratings array
  LOG:  1493284381
  Passed!

When you make a state variable public, the compiler automatically generates a getter function as described in the documentation https://solidity.readthedocs.io/en/latest/contracts.html#getter-functions:

function ratings(uint arg1, uint arg2) returns (uint rating, address reviewer, string description, uint dateAdded) {
    rating = ratings[arg1][arg2].rating;
    reviewer = ratings[arg1][arg2].reviewer;
    description = ratings[arg1][arg2].description;
    dateAdded = ratings[arg1][arg2].dateAdded;
}

You can access the member data in Solidity like this: var (rating, reviewer, description, dateAdded) = reviews.ratings(0, 0);

If you are using Truffle then the generated bindings treat tuples as arrays, so you can access the member data in JS like this:

var Reviews = artifacts.require("./Reviews.sol");

contract('Reviews', function(accounts) {
  it("should return the correct book rating", function() {
    var reviews;
    return Reviews.deployed().then(function(instance) {
      reviews = instance;
      return reviews.rateBook(0, 5, "This book changed my life");
    }).then(function() {
      return reviews.ratings.call(0, 0);
    }).then(function(review) {
      assert.equal(review[0], 5, "5 wasn't the rating for the book with id 0");
    });
  });
});

Likewise, a Truffle themed test would work like this:

pragma solidity ^0.4.2;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Reviews.sol";

contract TestReviews {

  function testRateBookUsingDeployedContract() {
    Reviews reviews = Reviews(DeployedAddresses.Reviews());

    reviews.rateBook(0, 5, "Test comment");
    var (rating, reviewer, description, dateAdded) = reviews.ratings(0, 0);

    Assert.equal(rating, 5, "Book should have a rating of 5");
  }

}

On the command line you can simply run these tests using truffle test and you will get the following output:

Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Reviews.sol...
Compiling ./test/TestReviews.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...


  TestReviews
    ✓ testRateBookUsingDeployedContract (38ms)

  Contract: Reviews
    ✓ should return the correct book rating (65ms)


  2 passing (198ms)
  • One more question on this, how do I access ratings outside of contract? I'm writing a test case in JS and this is how I was trying to but every time it is coming as undefined. // lms is the constructor of my contract let rating = await lms.ratings[1][accounts[0]].rating; //code fails here itself let description = await lms.ratings[1][accounts[0]].description; console.log(rating, description); – Sanchit Apr 27 '17 at 10:19
  • I've updated the answer with the information. – blockchaindev Apr 27 '17 at 11:30
  • Awesome! Thank you so much for explaining this. You are the Man! – Sanchit Apr 27 '17 at 19:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.