No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Arrays y mappings

17/22
Recursos

Un contrato inteligente puede almacenar cientos o miles de datos a lo largo de su vida 煤til. En estos casos debemos implementar estructuras de datos que nos permitan manipular gran cantidad de datos de forma f谩cil y organizada.

Qu茅 son los array o vectores de datos

La primera estructura de datos, t铆pica de cualquier lenguaje de programaci贸n, son los Arrays. Los arrays almacenan de forma secuencial datos de un mismo tipo y los mismos pueden ser accedidos a trav茅s de su 铆ndice o posici贸n dentro del mismo. Pueden ser de una longitud determinada o de longitud din谩mica dependiendo la necesidad.

// Array de n煤meros de hasta 3 posiciones
uint[3] numbers = [1, 2, 3];

// Array de n煤meros de longitud variable
uint[] numbers;

En cualquier tipo de array, puedes agregar o quitar elementos utilizando los m茅todos push(x) y pop().

push(x) agrega un nuevo elemento pasado como argumento al final del array, mientras que pop() remueve el 煤ltimo elemento.

El acceso a estos datos debe realizarse a trav茅s del 铆ndice del elemento comenzando por cero. Si tenemos el array string words = ["Bienvenido", "a", "Platzi"], el elemento 2 corresponde al string 鈥淧latzi鈥 y accedes a este con la sintaxis words[2].

Finalmente, puedes conocer la longitud de un array a trav茅s de su m茅todo myArr.length. El mismo devolver谩 un n煤mero entero que representa el tama帽o del mismo.

Qu茅 son los mappings o asignaciones de datos

Los arrays tiene sus limitaciones. Si tenemos un array con miles de datos, acceder a un valor en el medio de este puede ser costoso. Tener que recorrer todo el mismo para encontrar un valor requiere procesamiento y, por ende, consumo de gas. Los Mappings solucionan este problema permitiendo asignar valores a una clave 煤nica para acceder al dato.

Similar a un objeto donde acceder a sus propiedades a trav茅s de un nombre espec铆fico. Los mappings permiten utilizar cualquier tipo de clave para acceder a un valor y el mismo puede crecer y guardar tantos valores como necesitemos.

La declaraci贸n de un mapping requiere de especificar el tipo de dato de la clave, y el tipo de dato del valor que esta guardar谩.

mapping(string => uint) public myMapping;

Uno de los usos m谩s habituales de los mappings es para guardar el balance econ贸mico de una cuenta en el contrato. Cada clave guarda una address y el valor es el valor en ETH que esta posee.

mapping(address => uint) public balances;

El contenido del mapping puedes imaginarlo de la siguiente manera.

balances = {
    "direccion123": 1000,
    "direccion456": 2000,
    "direccion789": 5000
}

A trav茅s de direccion123, direccion456 y direccion789 puedes acceder r谩pidamente al balance de cada cuenta y su llamado se realiza de la forma balances["direccion123"].

Los mappings son completamente din谩micos, pudiendo almacenar un mapping dentro de otro.

mapping(address => mapping(uint => bool)) public nested;

Aumentando as铆 su complejidad y las posibilidades de guardar informaci贸n.

Una desventaja de los mapping es que no permiten ser recorridos como un array u obtener su longitud. Para esto puedes hacer uso de extensiones y utilidades para darle m谩s funcionalidad a esta estructura de datos si necesitas resolver casos de uso complejos.

Conclusi贸n

Tanto los Arrays como los Mappings tienen sus ventajas y desventajas y pueden ser implementados para almacenar informaci贸n. Comprender estas diferentes estructuras de datos que Solidity implementa nos permitir谩 tener m谩s herramientas para el desarrollo contratos inteligentes, organizar la informaci贸n y el acceso a estos.


Contribuci贸n creada por: Kevin Fiorentino (Platzi Contributor).

Aportes 15

Preguntas 4

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Como complemento al tema de los Arrays:

Los m茅todos push y pop solo funcionan en los arrays din谩micos. Para agregar/cambiar el valor de un elemento en un array fijo usaremos los corchetes y la posici贸n del elemento a cambiar. 馃槂

Un reto por cada clase ha sido una buena t茅cnica, ya que se va practicando lo aprendido, se revisa, se prueba y vamos adelantando el proyecto. Me ha gustado mucho.

Array

  • Contenedores que almacenan datos de un tipo especifico
  • Pueden ser de tamanios dinamicos o fijos
    • Fijos: uint[3] steps = [1,2,3];
    • Dinamicos: uint[] steps;
  • Se acceden a traves de su indice
  • Metodos push y pop solo funcionan en arrays dinamicos
steps.push(1) // [1]
steps.push(2) // [1,2]
steps.pop() // [1]

steps[0]  //1

.
Mapping

  • Permite almacenar datos asociando una llave:valor
    mapping (string -> string) countries;
countries['Felipe'] = 'Colombia';
countries['Laura']= 'Ecuador';

countries['Felipe'] // 'Colombia';
countries['Laura'] // 'Ecuador';

Excelente Profe, claro, preciso y me gusta mucho la din谩mica de poner retos y revisi贸n entre clases.

Los Arrays son una colecci贸n ordenada de datos de un solo tipo, mientras los mappings son Hashmaps, es decir, estructuras de clave valor. El correcto uso de estas estructuras de datos mejora el rendimiento de nuestro contrato, disminuyendo la carga de computo. Por ejemplo, buscar un address en un array tiene una complejidad de O(N) mientras que en un mapping con el address como clave tiene una complejidad de O(1). Me corrigen si no estoy en lo correcto jajaja. Saludos y buen contenido

Un array es un array de ac谩 a la China. No hay mucho que explicar.
Los Mapping utilizan el concepto de clave/valor, como si fuese un JSON o un Objeto al cual accedemos a sus datos a partir del nombre de las propiedades.

A diferencia de un objeto cl谩sico de JavaScript cuya correspondencia ser铆a un Struct, un mapping da una forma de objeto a un conjunto de datos regulares, que siempre tendr谩n el mismo tipo de Key para el mismo tipo de Value.

Este es mi avance con los arrays y las funciones nuevas del reto #7:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract Proyecto_Platzi{
    enum State {Open, Closed}

    struct projectInfo{
        string name;
        string description;
        string owner;
        address payable ownerWallet;
        State state;
        uint goal;
        uint currentFund;
    }

    projectInfo[] projects;

    mapping(string => uint) projectsContributions;

    modifier onlyOwner(uint index) {
        require(
            msg.sender == projects[index].ownerWallet,
            "Only owner can change the projects state"
        );
        //la funci贸n es insertada en donde aparece este simbolo
        _;
    }


    modifier differentToTheOwner(uint index) {
        require(
            msg.sender != projects[index].ownerWallet,
            "The owner can't fund the projects"
        );
        //la funci贸n es insertada en donde aparece este simbolo
        _;
    }

    event ChangeState(
        State previousState,
        State newState
    );

    event FundValueGiven(
        uint fundGiven,
        uint valueToGoal,
        string greetingMessage
    );

    function viewProjects(uint index) public view returns(projectInfo memory){
        return projects[index];
    }

    function viewProjectFund(string calldata projectName) public view returns(uint){
        return projectsContributions[projectName];
    }


    function createProject(string calldata _name, string calldata _owner, string calldata _description, uint _goal, State _state) public {
        projectInfo memory newProject = projectInfo(_name, _description, _owner, payable(msg.sender), _state, _goal, 0);
        projects.push(newProject);
    }

    function fundProject(uint ProjectIndex) public payable differentToTheOwner(ProjectIndex){
        require(projects[ProjectIndex].state != State.Closed, "Sorry, this projects is closed and cannot receive funds");
        require(msg.value > 0, "Sorry, fund value must be greater than 0, try again");
        projects[ProjectIndex].ownerWallet.transfer(msg.value);
        projects[ProjectIndex].currentFund += msg.value;
        projectsContributions[projects[ProjectIndex].name] = projects[ProjectIndex].currentFund;
        emit FundValueGiven(msg.value, (projects[ProjectIndex].goal - msg.value), "Thanks for your contribution");
    }

    function changeProjectState(State newState, uint ProjectIndex) public onlyOwner(ProjectIndex){
        require(projects[ProjectIndex].state != newState, "Sorry, to change the state you must put a different state from the current one");
        emit ChangeState(projects[ProjectIndex].state, newState);
        projects[ProjectIndex].state = newState;
    }
}
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract CrowdFunding {
    enum State { Active, Inactive }

    struct Contributors {
        address payable contributor;
        uint256 amount;
        uint256 timestamp;
    }

    struct CrowdFundingEntity {
        string projectName;
        State state;
        address payable author;
        uint256 balance;
        Contributors[] contributors;
    }

    CrowdFundingEntity public project;

    constructor(string memory _projectName, State _state) {
        project.projectName = _projectName;
        project.state = _state;
        project.author = payable(msg.sender);
    }

    error InvalidAmount (string message);

    event NewContributionReceived (
        address contributor,
        uint256 amount,
        uint256 balance
    );

    modifier onlyOwner () {
        require(msg.sender != project.author, "Que raro mandarte dinero a ti mismo");
        _;
    }

    function fundMe() public onlyOwner payable {
        require(project.state == State.Active, "El proyecto ya no esta activo, lo hubieses hecho antes BRO.");
        if (msg.value <= 0) {
            revert InvalidAmount("Estas aportando 0");
        }
        project.author.transfer(msg.value);
        project.balance += msg.value;
        emit NewContributionReceived(msg.sender, msg.value, project.balance);
        Contributors memory newContributor;
        newContributor.contributor = payable(msg.sender);
        newContributor.amount = msg.value;
        newContributor.timestamp = block.timestamp;

        project.contributors.push(newContributor);
    }

    function getContributors() public view returns (Contributors[] memory) {
        return project.contributors;
    }

    function getBalance() public view returns (uint256) {
        return project.balance;
    }

    function getProjectName() public view returns (string memory) {
        return project.projectName;
    }

    function changeProjectState (State _newState) public {
        project.state = _newState;
    }

    function changeProjectName (string memory _newProjectName) public {
        require(bytes(_newProjectName).length >= 3, 'El nombre del proyecto es demasiado corto');
        project.projectName = _newProjectName;
    }
}

RESUMEN CLASE 17:
ARRAYS Y MAPPINGS

I.- ARRAYS

  • Almacenan datos de un tipo.

  • Su tama帽o puede ser din谩mico o fijo.

  • Se accede al valor usando un 铆ndice.

II.- MAPPINGS

  • Asocian una 鈥渓lave鈥 con un valor.

  • El valor se accede usando la llave.

  • Solo se pueden almacenar en el storage.

RETO 7:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract CrowdFunding {
    enum FundraisingState {
        Opened,
        Closed
    }

    struct Contribution {
        address contributor;
        uint256 value;
    }

    struct Project {
        string id;
        string name;
        string description;
        address payable author;
        FundraisingState state;
        uint256 funds;
        uint256 fundraisingGoal;
    }

    Project[] public projects;
    mapping(string => Contribution[]) public contributions;

    event ProjectCreated(
        string projectId,
        string name,
        string description,
        uint256 fundraisingGoal
    );

    event ProjectFunded(string projectId, uint256 value);

    event ProjectStateChanged(string id, FundraisingState state);

    modifier isAuthor(uint256 projectIndex) {
        require(
            projects[projectIndex].author == msg.sender,
            "You need to be the project author"
        );
        _;
    }

    modifier isNotAuthor(uint256 projectIndex) {
        require(
            projects[projectIndex].author != msg.sender,
            "As author you can not fund your own project"
        );
        _;
    }

    function createProject(
        string calldata id,
        string calldata name,
        string calldata description,
        uint256 fundraisingGoal
    ) public {
        require(fundraisingGoal > 0, "fundraising goal must be greater than 0");
        Project memory project = Project(
            id,
            name,
            description,
            payable(msg.sender),
            FundraisingState.Opened,
            0,
            fundraisingGoal
        );
        projects.push(project);
        emit ProjectCreated(id, name, description, fundraisingGoal);
    }

    function fundProject(uint256 projectIndex)
        public
        payable
        isNotAuthor(projectIndex)
    {
        Project memory project = projects[projectIndex];
        require(
            project.state != FundraisingState.Closed,
            "The project can not receive funds"
        );
        require(msg.value > 0, "Fund value must be greater than 0");
        project.author.transfer(msg.value);
        project.funds += msg.value;
        projects[projectIndex] = project;

        contributions[project.id].push(Contribution(msg.sender, msg.value));

        emit ProjectFunded(project.id, msg.value);
    }

    function changeProjectState(FundraisingState newState, uint256 projectIndex)
        public
        isAuthor(projectIndex)
    {
        Project memory project = projects[projectIndex];
        require(project.state != newState, "New state must be different");
        project.state = newState;
        projects[projectIndex] = project;
        emit ProjectStateChanged(project.id, newState);
    }
}

on.Este cambio a array si me quem贸 las pesta帽as jaja, pero ah铆 se los dejo por si a alguno le sirve la forma en la que lo abord茅. Haciendo este ejercicio me di cuenta que al igual que en Java no se puede comparar los strings con el ==, que los emit sirven para debuggear y que para ver el contenido de un array hay que crear una funci贸n.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract Crowdfunding {
    //Array of projects
    CrowdfundingProject [] projects;

    //Mapping of donations
    mapping (string => uint256) donations;

    //Crowdfunding Project structure
    struct CrowdfundingProject{
        address payable projectAddress;
        string projectName;
        uint256 fundraisingGoal;
        uint256 moneyReceived;
        ProjectState projectState;
    }

    //Enum - Project States 
    enum ProjectState {Opened, Closed}

    //Modifier to validate the project with given name belong to sender address
    modifier onlyOwner(string calldata _projectName){
        bool isProjectOwner = false;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName)) && projects[i].projectAddress == msg.sender){
                isProjectOwner = true;
            }
        }
        require(isProjectOwner == true, "Only owner can do this action");
        _;
    }

    //Modifier to validate the project with given name does not belong to sender address
    modifier notOwnProject(string calldata _projectName) {
        bool isOwnProject = false;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName)) && projects[i].projectAddress == msg.sender){
                isOwnProject = true;
            }
        }
        require(isOwnProject == false, "Owner cannot fund his own project");
        _;
    }

    //Event when a project is funded
    event FundedProject(
        address contributor,
        uint256 donation
    );

    //Event when the project state changed
    event ProjectStateChanged(
        address author,
        ProjectState newState
    );

    //Function to create a project
    function createProject (string calldata projectName, uint256 _fundraisingGoal) public {
        CrowdfundingProject memory crowdfundingProject = CrowdfundingProject(payable(msg.sender), projectName, _fundraisingGoal, 0, ProjectState.Opened);
        projects.push(crowdfundingProject);

    }

    //Function to fund a project
    function fundProject (string calldata _projectName) public notOwnProject (_projectName) payable{
        require(bytes(_projectName).length>0, "The project name is mandatory");
        require(msg.value > 0, "The donation should be greater than zero");

        uint index;
        bool foundProject = false;
        CrowdfundingProject memory currentProject;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName))){
                currentProject = projects[i];
                foundProject = true;
                index = i;
            }
        }

        emit ProjectStateChanged(msg.sender, currentProject.projectState);

        require(foundProject == true, "The project was not found");
        require(currentProject.projectState == ProjectState.Opened, "Crodwfunding is closed");
        require (currentProject.moneyReceived + msg.value <= currentProject.fundraisingGoal, "Fundarising is almost full. Fund with less money.");
        currentProject.projectAddress.transfer(msg.value);
        currentProject.moneyReceived = currentProject.moneyReceived + msg.value;
        //Update array and mapping
        projects[index] = currentProject;
        donations[_projectName] = currentProject.moneyReceived;
        emit FundedProject(msg.sender, msg.value);
    }

    //Function to change the project state
    function changeProjectState(string calldata _projectName) public onlyOwner (_projectName) {

        uint index;
        CrowdfundingProject memory currentProject;
        for(uint i = 0; i < projects.length; i++){
            if(keccak256(bytes(projects[i].projectName)) == keccak256(bytes(_projectName))){
                currentProject = projects[i];
                index = i;
            }
        }

        if(currentProject.projectState == ProjectState.Opened){
            currentProject.projectState = ProjectState.Closed;
            projects[index] = currentProject;
        }else{
            currentProject.projectState = ProjectState.Opened;
            projects[index] = currentProject;
        }
        emit ProjectStateChanged(msg.sender, currentProject.projectState);
    }

    //Function to get all projects
    function getAllProjects() public view returns (CrowdfundingProject [] memory) {
        return projects;
    }

    //Function to get donations for a specific project
    function getDonationsForProject(string calldata _projectName) public view returns (uint256) {
        return donations[_projectName];
    }

}

Este modifier puede servir para que no se ponga el mismo proyecto varias veces (con el project name como identificador)

    modifier distinctProjects(string _name) {
        bool isDistinct = true;
        for (uint i = 0; i < projects.length; i++) {
            if (projects[i].name == _name) {
                isDistinct = false;
            }
        }
        require(isDistinct, "This project already exists");
        _;
    }

Mapping

Arrays

los arrays son como contenedores de tipos especifico