DEV Community

loading...

Looping with Foreach

Jesse Phillips
Senior Quality Assurance (SDET) ¶ Avid hobby D programmer ¶ Telling people what to do because I am right.
Updated on ・4 min read

Traversing data is one of the most common elements of programming. Generally this means you have some sort of collection. If you are interested in seeing the different ways languages approach the foreach loop visit Rosetta Code. However I do not think it does a good job of showing the nuances of the approach. I will start with the basics in D, and use D as the bed for comparing differences.

List of Items

// D
import std;
void main()
    auto vehicles = ["car", "truck", "boat", "rv"];
    foreach(vehicle; vehicles) {
        writeln(vehicle);
    } 
}
Enter fullscreen mode Exit fullscreen mode

It uses a specific keyword foreach and separates the variable declaration (on the left) from the iterable (on the right) with a semicolon. D has type inference and does not require a type to be specified for the variable.

// Javascript 
["car", "truck", "boat", "rv"].forEach(
  function (vehicle) {
    console.log(vehicle);
  }
);
Enter fullscreen mode Exit fullscreen mode

Here we see that foreach provided as a function which takes a lambda. This method lives on the array type and is not available to other iterable types. D provides a generic template to operate in this manner.

// D
vehicles.each!(vehicle =>
writeln(vehicle));
Enter fullscreen mode Exit fullscreen mode
// C#
string[] things = {"car", "truck", "boat", "rv"};

foreach (var thing in things)
{
    Console.WriteLine(thing);
}
Enter fullscreen mode Exit fullscreen mode

C# also has type inference which is explicitly requested. in separate the variable from the iterable. If you are dealing with the List type C# provides a ForEach method just like Javascript, unlike D it is not available to other iterable types.

-- Lua 
auto vehicles = {'car', 'truck', 'boat', 'rv'} 
for key, vehicle in ipairs(vehicles) do                                                                         
  print(key, vehicle)
end
Enter fullscreen mode Exit fullscreen mode

Lua uses just the keyword for probably because it does not provide a C style for loop.

It also has ipairs to iterate over positive keys in order. Lua does not have arrays or lists, the only data structure is a table (conceptually an associative array). This means iteration is defined by that extra call.

Dictionary

-- Lua 
vehicleMake= {truck='Dodge', car='Honda', boat='Schaefer', rv='Forest River' }
for key, value in pairs(vehicleMake) do                       
  print(key, value)
end
Enter fullscreen mode Exit fullscreen mode

Instead here we use pairs to get at everything in the table. Let's revisit the other languages.

// C#
var vehicleMake = new Dictionary<string, string>
{
    ["car"] = "Honda",
    ["truck"] = "Dodge",
    ["boat"] = "Schaefer",
    ["rv"] = "Forest River",
}

foreach(var item in vehicleMake) {
    System.Output.WriteLine($"{item.Key} {item.Value}");
}

Enter fullscreen mode Exit fullscreen mode

In this case we see that the key and value are provided in a wrapping type and is not automatically expanded within foreach.

// D
auto vehicleMake = ["truck" : "Dodge", "car" : "Honda", "boat" : "Schaefer", "rv" : "Forest River"];
foreach(key, value; vehicleMake) {                    
  writeln(key, ": ", value);
}
Enter fullscreen mode Exit fullscreen mode

D maintains the same syntax for dictionary iteration. But it is not only available to dictionary, A range returning a tuple will expand within foreach.

//D 
auto vehicleMake = [tuple("truck", "Dodge", 8), 
tuple("car", "Honda", 4), 
tuple("boat", "Schaefer", 5), 
tuple("rv", "Forest River", 8)].map!(x => x);
foreach(key, value, num; vehicleMake) {
    writeln(key, ": ", value, "-", num);
}
Enter fullscreen mode Exit fullscreen mode

The comma operator is deprecated and there is hope to use it for expansion during assignments, not just in foreach.

You should also notice the identity map used on the array. This is because arrays get special treatment with foreach and so tuple expansion does not happen and this trick turns it into a range.

Another language which takes advantage of tuples to make this work is Python.

#Python 
vehicleMake = {"truck" : "Dodge", "car" : "Honda", "boat" : "Schaefer", "rv" : "Forest River"}

for key, value in vehicleMake.items():
    print(key, value)
Enter fullscreen mode Exit fullscreen mode

The call for items() provides a tuple used to expand into key and value.

Changing Types

In this little experiment I am going to utilize the basic for each with the two types (array, associative array). You be the judge.

// D
auto arr = ["one", "two"];
auto dict = [1:"one", 2:"two"];

writeln("Array:");
foreach(value; arr) writeln(value);

writeln("Dictionary:");
foreach(value; dict) writeln(value);

/* // output 
Array:
one
two
Dictionary:
two
one
*/
Enter fullscreen mode Exit fullscreen mode
// C#
using System;
using System.Collections.Generic;

public class Program {
    public static void Main() {

var arr = new string[] {"one", "two"};
var dict = new Dictionary<int, string>() { [1] ="one", [2] ="two"};

Console.WriteLine("Array:");
foreach(var val in arr) 
   Console.WriteLine(val);

Console.WriteLine("Dictionary:");
foreach(var val in dict) 
   Console.WriteLine(val);

   } 
} 

/* // output 
Array:
one
two
Dictionary:
[1, one]
[2, two]
*/
Enter fullscreen mode Exit fullscreen mode
-- Lua 
arr = {"one", "two"}
dict = {[1] ="one", [2] ="two", [0]="zero" } 

print("Array:");
for value in ipairs(arr) do print(value) end

print("Dictionary:");
for value in ipairs(dict) do print(value) end

--[[ output
Array:
1
2
Dictionary:
1
2
--]]
Enter fullscreen mode Exit fullscreen mode

Notice the dictionary does not print 0.

I needed to modify the Lua dictionary in order to demonstrate this languages odd behavior. Because I used integers as a key Lua sees it the same as the array (they are the same because they are both tables). Other dictionaries with strings would not print if you use ipairs.

# Python
arr = {"one", "two"} 
dict = {1:"one", 2:"two"} 

print("Array:") 
for value in arr:
    print(value)

print("Dictionary:") 
for key in dict:
    print(key)

# output 
#Array:
#two
#one
#Dictionary:
#1
#2
Enter fullscreen mode Exit fullscreen mode

Python provides the dictionary key rather than the value as other languages have.

Discussion (0)