The Crystal Programming Language Forum

Make an array of Hash

Hello, I have a problem today. I try to initialize an empty Array,and this array will have inside hash. But how I can initialize that properly, because when I try that, always I have an error.

See. This code is an example of what I want:

SoftwaresDatabase = Array(Hash).new

SoftwaresDatabase << { 
"Name" => "Binutils",
"Architectures" => ["x86_64"],
"Description" => "The GNU collection of binary tools",
"Website" => "",
"DownloadLinks" => [""],
"SignatureLinks" => [""],
"Options" => {  "Pass1" => {"Description" => "Build the first pass to make a system from scratch",
                            "Enabled" => false},
                "Pass2" => {"Description" => "Build the second pass to make a system from scratch",
                            "Enabled" => false},
"Download" => download,
"Check" => check,
"Extract" => extract,
"Prepare" => prepare,
"Configure" => configure,
"Build" => build,
"Install" => install,
"Uninstall" => uninstall,
"EnableOption" => enableOption,
"DisableOption" => disableOption

def download

def check
    `gpg binutils-2.37.tar.xz.sig`

def extract
    `tar -xf binutils-2.37.tar.xz`

def prepare

def configure
    if SoftwareInformation["Options"]["Pass1"]["Enabled"] == true
        `../configure   --prefix=#{ism.settings.toolsPath}
    elsif SoftwareInformation["Options"]["Pass2"]["Enabled"] == true
        `../configure   --prefix=/usr
        `../configure   --prefix=/usr

def build
    if SoftwareInformation["Options"]["Pass1"]["Enabled"] == true || SoftwareInformation["Options"]["Pass2"]["Enabled"] == true
        `make tooldir=/usr`

def install
    if SoftwareInformation["Options"]["Pass1"]["Enabled"] == true
        `make -j1 install`
    elsif SoftwareInformation["Options"]["Pass2"]["Enabled"] == true
        `make DESTDIR=#{ism.settings.rootPath} install -j1`
        `install -vm755 libctf/.libs/ #{ism.settings.rootPath}/usr/lib`
        `make tooldir=/usr install -j1`
        `rm -fv /usr/lib/lib{bfd,ctf,ctf-nobfd,opcodes}.a`

def uninstall


def enableOption(option)
    if SoftwareInformation["Options"][option] == "Pass1"
        SoftwareInformation["Options"]["Pass1"]["Enabled"] = true
        SoftwareInformation["Options"]["Pass2"]["Enabled"] = false
    elsif SoftwareInformation["Options"][option] == "Pass2"
        SoftwareInformation["Options"]["Pass2"]["Enabled"] = true
        SoftwareInformation["Options"]["Pass1"]["Enabled"] = false
        SoftwareInformation["Options"][option]["Enabled"] = true

def disableOption(option)
    SoftwareInformation["Options"][option]["Enabled"] = false

The error:

~/Documents/Programmation/ISM/Softwares/Binutils ❯❯❯ crystal                                                                                                                                                          ✘ 1 master ✱ ◼
Showing last frame. Use --error-trace for full trace.


 1 | SoftwaresDatabase = Array(Hash).new
Error: can't use Hash(K, V) as generic type argument yet, use a more specific type

This isn’t something you can do, you’d need to more specifically type the hash, like the error is suggesting. E.g. Array(Hash(String, String | Array(String) | ...)). However this is going to be a nightmare and I would highly suggest NOT using hashes like this. You are only going to have a hard time.

Instead I would suggest creating objects to manage this data:

class SoftwareProgram
  getter name : String
  getter architectures : Array(String)
  getter website : String

  def initialize(@name : String, @architectures : Array(String), @website : String); end

databases = Array(SoftwareProgram).new

databases <<"Binutils", ["x86_64"], "")
# ...
1 Like

Okay. I think I need your advices, because I’m learning by myself from a long time how to programming, and I think my dark point is how I manage my data.

To explain, the file I linked to you is a file to describe how to download, build and install Binutils from the source to my software manager.

I don’t really know how to manage properly the data, but I guess if I register the data as class, it’s can be a problem later if I update this data no ? (I don’t want to use SQL or something like that )

Or I had an idea, maybe when I need to install a software, I can rebuild first the crystal file to avoid the problem with a new version of my class ?

Maybe i’m missing something but I don’t see how using a type would be any different than using a Hash. You’re going to need to recompile it if the data changes regardless of if you’re storing it in a Hash or another type unless you implement a way to read in the data at runtime, such as SQL or yaml etc.

If the data is immutable I’d suggest using a struct, or Top Level Namespace - Crystal 1.1.1. Otherwise a class would be the way to go.

Your logic itself doesn’t need to change that much as the types are just more explicitly representing the data already in the hash.

I.e. instead of if SoftwareInformation["Options"]["Pass1"]["Enabled"] == true it would be like SoftwareInformation.options.pass1.enabled?. Granted this is just an example. If the options are not actually hard coded you could still do something like SoftwareInformation.option("pass1").enabled? where the options are internally stored as a Hash(String, Option).

EDIT: To clarify, it’s not that using hashes is bad per say, but more so how you are using them. I.e. deeply nested with a lot of different types.