Hash from two arrays: Difference between revisions

From Rosetta Code
Content added Content deleted
(added vbscript & vb; omit from powerbasic & basic)
(→‎Visual Basic: added libheader template)
Line 595: Line 595:
{{trans|VBScript}}
{{trans|VBScript}}


This requires a reference to the Microsoft Scripting Runtime (scrrun.dll).
{{libheader|Microsoft Scripting Runtime (scrrun.dll)}}


The VBScript version can be used in Visual Basic unchanged, but this version explicitly declares <code>dict</code> as a dictionary, rather than setting a Variant to a Dictionary object.
The VBScript version can be used in Visual Basic unchanged, but this version explicitly declares <code>dict</code> as a dictionary, rather than setting a Variant to a Dictionary object.

Revision as of 21:03, 2 October 2009

Task
Hash from two arrays
You are encouraged to solve this task according to the task description, using any language you may know.

Using two Arrays of equal length, create a Hash object where the elements from one array (the keys) are linked to the elements of the other (the values)

ActionScript

<lang actionscript> package {

   public class MyClass
   {
       public static function main():Void
       {
           var hash:Object = new Object();
           var keys:Array = new Array("a", "b", "c");
           var values:Array = new Array(1, 2, 3);
           
           for (var i:int = 0; i < keys.length(); i++)
               hash[keys[i]] = values[i]; 
       }
   }

} </lang>

Ada

Works with: GNAT version GPL 2007

<lang ada>

with Ada.Strings.Hash;
with Ada.Containers.Hashed_Maps;
with Ada.Text_Io;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Hash_Map_Test is
   function Equivalent_Key (Left, Right : Unbounded_String) return Boolean is
   begin
      return Left = Right;
   end Equivalent_Key;
  
   function Hash_Func(Key : Unbounded_String) return Ada.Containers.Hash_Type is
   begin
      return Ada.Strings.Hash(To_String(Key));
   end Hash_Func;
  
   package My_Hash is new Ada.Containers.Hashed_Maps(Key_Type => Unbounded_String,
      Element_Type => Unbounded_String,
      Hash => Hash_Func,
      Equivalent_Keys => Equivalent_Key);
     
   type String_Array is array(Positive range <>) of Unbounded_String;
     
   Hash : My_Hash.Map;
   Key_List : String_Array := (To_Unbounded_String("foo"), 
      To_Unbounded_String("bar"),
      To_Unbounded_String("val"));
     
   Element_List : String_Array := (To_Unbounded_String("little"), 
      To_Unbounded_String("miss"), 
      To_Unbounded_String("muffet"));
     
begin
   for I in Key_List'range loop
      Hash.Insert(Key => (Key_List(I)),
         New_Item => (Element_List(I)));
   end loop;
   for I in Key_List'range loop
      Ada.Text_Io.Put_Line(To_String(Key_List(I)) & " => " &
         To_String(Hash.Element(Key_List(I))));
   end loop;
  
end Hash_Map_Test;

</lang>

AWK

Awk arrays are used for both lists and hash maps. <lang awk> $ awk 'BEGIN{split("one two three",a);

            split("1 2 3",b);
            for(i=1;i in a;i++){c[a[i]]=b[i]};
            for(i in c)print i,c[i]
            }'

three 3 two 2 one 1</lang>

C++

By strict definition a std::map is not a hash, but it provides the same functionality. The C++-200x update to the C++ standard is incorporating hashes. When they are standardized the code below can change std::map to std::unordered_map and this will technically be a hash table. The core idea, turning two sequences into an associative mapping, is valid either way.

<lang cpp> #include <map>

#include <string>

int
main( int argc, char* argv[] )
{
 std::string keys[] = { "1", "2", "3" } ;
 std::string vals[] = { "a", "b", "c" } ;
 
 std::map< std::string, std::string > hash ;
 
 for( int i = 0 ; i < 3 ; i++ )
 {
  hash[ keys[i] ] = vals[i] ;
 }
}</lang>

Alternatively: <lang cpp> #include <map> // for std::map

#include <algorithm> // for std::transform
#include <string>    // for std::string
#include <utility>   // for std::make_pair

int main()
{
  std::string keys[] = { "one", "two", "three" };
  std::string vals[] = { "foo", "bar", "baz" };

  std::map<std::string, std::string> hash;

  std::transform(keys, keys+3,
                 vals,
                 std::inserter(hash, hash.end()),
                 std::make_pair<std::string, std::string>);
}</lang>

C#

<lang csharp> System.Collections.HashTable h = new System.Collections.HashTable();

 string[] arg_keys = {"foo","bar","val"};
 string[] arg_values = {"little", "miss", "muffet"};
   
 //Some basic error checking
 int arg_length = 0;
 if ( arg_keys.Length == arg_values.Length ) {
   arg_length = arg_keys.Length;
 }
 
 for( int i = 0; i < arg_length; i++ ){
   h.add( arg_keys[i], arg_values[i] ); 
 }</lang>

Alternate way of adding values

<lang csharp> for( int i = 0; i < arg_length; i++ ){

   h[ arg_keys[i] ] = arg_values[i]; 
 }</lang>

Common Lisp

<lang lisp>(defun rosetta-code-hash-from-two-arrays (vector-1 vector-2 &key (test 'eql))

 (assert (= (length vector-1) (length vector-2)))
 (let ((table (make-hash-table :test test :size (length vector-1))))
   (map nil (lambda (k v) (setf (gethash k table) v))
            vector-1 vector-2)
   table))</lang>

Or, using cl:loop:

<lang lisp>(defun rosetta-code-hash-from-two-arrays (vector-1 vector-2 &key (test 'eql))

 (loop initially (assert (= (length vector-1) (length vector-2)))
       with table = (make-hash-table :test test :size (length vector-1))
       for k across vector-1
       for v across vector-2
       do (setf (gethash k table) v)
       finally (return table)))</lang>

In Common Lisp terminology, a vector is a one-dimensional array.

D

<lang d> string[] keys = ["one", "two", "three"]

int[] values  = [1, 2, 3];

int[string] hash;

foreach(idx, key; keys)
  hash[key] = values[idx];</lang>

E

def keys := ["one", "two", "three"]
def values := [1, 2, 3]
__makeMap.fromColumns(keys, values)

Factor

USING: hashtables ;
{ "one" "two" "three" } { 1 2 3 } zip >hashtable

Groovy

<lang groovy> keys = ['a','b','c']

vals = ['aaa', 'bbb', 'ccc']
hash = [:]
i = 0
keys.each { entry ->
 hash.put(entry, vals[i++]) 
}</lang>

Haskell

Works with: GHCi version 6.6

<lang haskell> import Data.Map

makeMap ks vs = fromList $ zip ks vs
mymap = makeMap ['a','b','c'] [1,2,3]</lang>

J

hash=: vals {~ keys&i.

For example:

   keys=: 10?.100 
   vals=: > ;:'zero one two three four five six seven eight nine'
   hash=: vals {~ keys&i.

   keys
46 99 23 62 42 44 12 5 68 63
   $vals
10 5

   hash 46
zero 
   hash 99
one  
   hash 63 5 12 5 23
nine 
seven
six  
seven
two

Here, keys is a list of 10 integers between 0 and 99 chosen at random without repetition, and vals is a 10 by 5 character matrix.

Java

<lang java> import java.util.HashMap;

public static void main(String[] args){
	String[] keys= {"a", "b", "c"};
	int[] vals= {1, 2, 3};
	HashMap<String, Integer> hash= new HashMap<String, Integer>();

	for(int i= 0; i < keys.length; i++){
	   hash.put(keys[i], vals[i]);
	}
}</lang>

JavaScript

<lang javascript> var keys = ['a', 'b', 'c'];

var values = [1, 2, 3];
var map = {};
for(var i in keys) {
  map[ keys[i] ] = values[i];
}</lang>

Objective-C

<lang objc> NSArray *keys = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];

NSArray *values = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],
                                            [NSNumber numberWithInt:2],
                                            [NSNumber numberWithInt:3], nil];
NSDictionary *dict = [NSDictionary dictionaryWithObjects:values forKeys:keys];</lang>

OCaml

The idiomatic solution uses lists rather than arrays.

<lang ocaml> let keys = [ "foo"; "bar"; "baz" ]

 and vals = [ 16384; 32768; 65536 ]
 and hash = Hashtbl.create 0;;
 List.iter2 (Hashtbl.add hash) keys vals;;</lang>

In the extremely unlikely event that it was actually necessary to use arrays, the solution would become slightly less elegant: (except using the ExtLib which provides the equivalent Array.iter2)

<lang ocaml> let keys = [| "foo"; "bar"; "baz" |]

 and vals = [| 16384; 32768; 65536 |]
 and hash = Hashtbl.create 0;;
 for i = 0 to Array.length keys - 1 do
   Hashtbl.add hash keys.(i) vals.(i)
 done;;</lang>

In either case, an exception is raised if the inputs are different lengths.

If you want to use functional binary search trees instead of hash tables:

<lang ocaml> module StringMap = Map.Make (String);;

 let keys = [ "foo"; "bar"; "baz" ]
 and vals = [ 16384; 32768; 65536 ]
 and map = StringMap.empty;;
 let map = List.fold_right2 StringMap.add keys vals map;;</lang>

Perl

Works with: Perl version 5

<lang perl> use List::MoreUtils qw(zip);

my @keys = qw(a b c);
my @vals = (1, 2, 3);
my %hash = zip @keys, @vals;</lang>

Using no modules: <lang perl> my %hash;

@hash{qw(a b c)} = (1, 2, 3);</lang>

PHP

Works with: PHP version 5

<lang php> $keys = array('a', 'b', 'c');

$values = array(1, 2, 3);
$hash = array_combine($keys, $values);</lang>
Works with: PHP version 4

<lang php> $keys = array('a', 'b', 'c');

$values = array(1, 2, 3);
$hash = array();
for ($idx = 0; $idx < count($keys); $idx++) {
  $hash[$keys[$idx]] = $values[$idx];
}</lang>

Pop11

vars keys = { 1 a b c};
vars vals = { 2 3 valb valc};
vars i;
;;; Create hash table
vars ht = newmapping([], 500, 0, true);
;;; Loop over input arrays (vectors)
for i from 1 to length(keys) do
  vals(i) -> ht(keys(i));
endfor;

PowerShell

<lang powershell>function create_hash ([array] $keys, [array] $values) {

   $h = @{}
   if ($keys.Length -ne $values.Length) {
       Write-Error -Message "Array lengths do not match" `
                   -Category InvalidData `
                   -TargetObject $values
   } else {
       for ($i = 0; $i -lt $keys.Length; $i++) {
           $h[$keys[$i]] = $values[$i]
       }
   }
   return $h

}</lang>

Prolog

<lang prolog> % this one with side effect hash table creation

-dynamic hash/2.

make_hash([],[]). make_hash([H|Q],[H1|Q1]):- assert(hash(H,H1)), make_hash(Q,Q1).

-make_hash([un,deux,trois],[[a,b,c],[d,e,f],[g,h,i]])


% this one without side effects

make_hash_pure([],[],[]). make_hash_pure([H|Q],[H1|Q1],[hash(H,H1)|R]):- make_hash_pure(Q,Q1,R).

-make_hash_pure([un,deux,trois],[[a,b,c],[d,e,f],[g,h,i]],L),findall(M,(member(M,L),assert(M)),L).

</lang>

Python

Works with: Python version 2.3+

<lang python> keys = ['a', 'b', 'c'] values = [1, 2, 3] hash = dict(zip(keys, values))

  1. Lazily:

from itertools import izip hash = dict(izip(keys, values)) </lang>

Works with: Python version 3.0+

Shows off the dict comprehensions in Python 3: <lang python> keys = ['a', 'b', 'c'] values = [1, 2, 3] hash = {key: value for key, value in zip(keys, values)} </lang>

Works with: Python

(any version)

<lang python>keys = ['a', 'b', 'c'] values = [1, 2, 3] hash = {} for i range(len(keys)):

   hash[keys[i]] = values[i]

</lang> The original (Ruby) example uses a range of different types as keys. Here is similar in python (run at the shell): <lang python>>>> class Hashable(object): def __hash__(self): return id(self) ^ 0xBEEF


>>> my_inst = Hashable() >>> my_int = 1 >>> my_complex = 0 + 1j >>> my_float = 1.2 >>> my_string = "Spam" >>> my_bool = True >>> my_unicode = u'Ham' >>> my_list = ['a', 7] >>> my_tuple = ( 0.0, 1.4 ) >>> my_set = set(my_list) >>> def my_func(): pass

>>> class my_class(object): pass

>>> keys = [my_inst, my_tuple, my_int, my_complex, my_float, my_string, my_bool, my_unicode, frozenset(my_set), tuple(my_list), my_func, my_class] >>> values = range(12) >>> d = dict(zip(keys, values)) >>> for key, value in d.items(): print key, ":", value

1 : 6 1j : 3 Ham : 7 Spam : 5 (0.0, 1.3999999999999999) : 1 frozenset(['a', 7]) : 8 1.2 : 4 ('a', 7) : 9 <function my_func at 0x0128E7B0> : 10 <class '__main__.my_class'> : 11 <__main__.Hashable object at 0x012AFC50> : 0 >>> </lang>

R

Assuming that the keys are coercible to character form, we can simply use the names attribute to create a hash. This example is taken from the Wikipedia page on hash tables. <lang r>

  1. Set up hash table

keys <- c("John Smith", "Lisa Smith", "Sam Doe", "Sandra Dee", "Ted Baker") values <- c(152, 1, 254, 152, 153) names(values) <- keys

  1. Get value corresponding to a key

values["Sam Doe"] # vals["Sam Doe"]

  1. Get all keys corresponding to a value

names(values)[values==152] # "John Smith" "Sandra Dee" </lang>

Raven

[ 'a' 'b' 'c' ] as $keys [ 1 2 3 ] as $vals
$keys $vals combine as $hash

Ruby

<lang ruby>keys=['hal',666,[1,2,3]] vals=['ibm','devil',123] hash = Hash[keys.zip(vals)] # Ruby 1.8.7 and later hash = Hash[*keys.zip(vals).flatten] # pre-1.8.7

  1. now hash => {'hal' => 'ibm', 666 => 'devil', [1,2,3] => 123}
  1. retrieve the value linked to the key [1,2,3]

puts hash[ [1,2,3] ]

  1. 123</lang>

Scala

val keys = Array(1, 2, 3)
val values = Array("A", "B", "C")
val map = Map(keys.zip(values) : _*)
// returns Map(1 -> "A", 2 -> "B", 3 -> "C")
// keys.zip(values) is an array of pairs : Array((1, "A"), (2, "B"), (3, "C"))
// Map(...) expects multiple pairs arguments. Syntax ": _*" tells the single argument contains multiple values.

Scheme

Using SRFI 69: <lang scheme>(define (lists->hash-table keys values . rest)

 (apply alist->hash-table (map cons keys values) rest))</lang>

Seed7

$ include "seed7_05.s7i";

const type: numericHash is hash [string] integer;
var numericHash: myHash is numericHash.value;

const proc: main is func
  local
    var array string: keyList is [] ("one", "two", "three");
    var array integer: valueList is [] (1, 2, 3);
    var integer: number is 0;
  begin
    for number range 1 to length(keyList) do
      myHash @:= [keyList[number]] valueList[number];
    end for;
  end func;

Standard ML

Works with: SML/NJ

Using functional binary search trees instead of hash tables:

<lang sml>structure StringMap = BinaryMapFn (struct

                                    type ord_key = string
                                    val compare = String.compare
                                  end);

val keys = [ "foo", "bar", "baz" ] and vals = [ 16384, 32768, 65536 ] and myMap = StringMap.empty;

val myMap = foldl StringMap.insert' myMap (ListPair.zipEq (keys, vals));</lang>

Works with: SML/NJ

Using hash tables:

<lang sml>exception NotFound;

val keys = [ "foo", "bar", "baz" ] and vals = [ 16384, 32768, 65536 ] and hash = HashTable.mkTable (HashString.hashString, op=) (42, NotFound);

ListPair.appEq (HashTable.insert hash) (keys, vals);</lang>

Tcl

Arrays in Tcl are automatically associative, i.e. there are no "not hashed arrays". If we can take "arrays of equal length" to mean "lists of equal length", then the task might look like this: <lang tcl>set keys [list fred bob joe] set values [list barber plumber tailor] array set arr {} foreach a $keys b $values { set arr($a) $b }</lang> Alternatively, a dictionary could be used: <lang tcl>foreach a $keys b $values {

   dict set jobs $a $b

}</lang>

UnixPipes

Using a sorted file as an associative array (see Creating an associative array for usage.)

cat <<VAL >p.values
apple
boy
cow
dog
elephant
VAL
cat <<KEYS >p.keys
a
b
c
d
e
KEYS
paste -d\   <(cat p.values | sort) <(cat p.keys | sort)

Ursala

There's a built in operator for this. <lang Ursala> keys = <'foo','bar','baz'> values = <12354,145430,76748>

hash_function = keys-$values </lang> test program: <lang Ursala>

  1. cast %nL

test = hash_function* <'bar','baz','foo','bar'> </lang> output:

<145430,76748,12354,145430>

VBScript

VBScript (and Visual Basic in general) calls hashes "dictionary objects".

<lang vb>Set dict = CreateObject("Scripting.Dictionary") os = Array("Windows", "Linux", "MacOS") owner = Array("Microsoft", "Linus Torvalds", "Apple") For n = 0 To 2

   dict.Add os(n), owner(n)

Next MsgBox dict.Item("Linux") MsgBox dict.Item("MacOS") MsgBox dict.Item("Windows")</lang>

Output:

Linus Torvalds
Apple
Microsoft

Visual Basic

Translation of: VBScript

The VBScript version can be used in Visual Basic unchanged, but this version explicitly declares dict as a dictionary, rather than setting a Variant to a Dictionary object.

Normally, I prefer to explicitly declare all variables, but in this instance I can't, because Array() doesn't like anything but variants. (In a real program, I probably wouldn't load the arrays with Array(), therefore allowing me to declare those arrays as strings, but it was a simple expendiant for this example.)

<lang vb>Dim dict As New Dictionary os = Array("Windows", "Linux", "MacOS") owner = Array("Microsoft", "Linus Torvalds", "Apple") For n = 0 To 2

   dict.Add os(n), owner(n)

Next Debug.Print dict.Item("Linux") Debug.Print dict.Item("MacOS") Debug.Print dict.Item("Windows")</lang>

Visual Basic .NET

<lang vb> Dim names = New String() {"Frank", "Tom", "Jones"}

       Dim grades = New Integer() {90, 87, 96}


       Dim hash = New Dictionary(Of String, Integer)
       For i = 0 To names.Length - 1
           hash.Add(names(i), grades(i))
       Next</lang>