Category Archives: JavaScript

JavaScript 32 – la vita segreta degli oggetti – 2

Continuo da qui, copio qui.

Costruttori
A more convenient way to create objects that derive from some shared prototype is to use a constructor. In JavaScript, calling a function with the new keyword in front of it causes it to be treated as a constructor. The constructor will have its this variable bound to a fresh object, and unless it explicitly returns another object value, this new object will be returned from the call.

An object created with new is said to be an instance of its constructor.

Here is a simple constructor for rabbits. It is a convention to capitalize the names of constructors so that they are easily distinguished from other functions (file co0.js).

function Rabbit(type) {
  this.type = type;
}

var killerRabbit = new Rabbit("killer");
var blackRabbit = new Rabbit("black");

console.log(blackRabbit.type);

Constructors (in fact, all functions) automatically get a property named prototype, which by default holds a plain, empty object that derives from Object.prototype. Every instance created with this constructor will have this object as its prototype. So to add a speak method to rabbits created with the Rabbit constructor, we can simply do this (co1.js):

function Rabbit(type) {
  this.type = type;
}
var blackRabbit = new Rabbit("black");

Rabbit.prototype.speak = function(line) {
  console.log("The " + this.type + " rabbit says '" +
              line + "'");
};

blackRabbit.speak("Doom...");

It is important to note the distinction between the way a prototype is associated with a constructor (through its prototype property) and the way objects have a prototype (which can be retrieved with Object.getPrototypeOf). The actual prototype of a constructor is Function.prototype since constructors are functions. Its prototype property will be the prototype of instances created through it but is not its own prototype.

Sostuituire (override) proprietà derivate
When you add a property to an object, whether it is present in the prototype or not, the property is added to the object itself, which will henceforth have it as its own property. If there is a property by the same name in the prototype, this property will no longer affect the object. The prototype itself is not changed (co2.js).

function Rabbit(type) {
  this.type = type;
}
var killerRabbit = new Rabbit("killer");
var blackRabbit = new Rabbit("black");

Rabbit.prototype.teeth = "small";
console.log(killerRabbit.teeth);

killerRabbit.teeth = "long, sharp, and bloody";
console.log(killerRabbit.teeth);
console.log(blackRabbit.teeth);
console.log(Rabbit.prototype.teeth);

The following diagram sketches the situation after this code has run. The Rabbit and Object prototypes lie behind killerRabbit as a kind of backdrop, where properties that are not found in the object itself can be looked up.

Overriding properties that exist in a prototype is often a useful thing to do. As the rabbit teeth example shows, it can be used to express exceptional properties in instances of a more generic class of objects, while letting the nonexceptional objects simply take a standard value from their prototype.

It is also used to give the standard function and array prototypes a different toString method than the basic object prototype (op0.js).

console.log(Array.prototype.toString ==
            Object.prototype.toString);
console.log([1, 2].toString());

Calling toString on an array gives a result similar to calling .join(",") on it —it puts commas between the values in the array. Directly calling Object.prototype.toString with an array produces a different string. That function doesn’t know about arrays, so it simply puts the word “object” and the name of the type between square brackets (op1.js).

console.log(Object.prototype.toString.call([1, 2]));

:mrgreen:

JavaScript 31 – la vita segreta degli oggetti – 1

Continuo da qui, copio qui.

When a programmer says “object”, this is a loaded term. In my profession, objects are a way of life, the subject of holy wars, and a beloved buzzword that still hasn’t quite lost its power.

To an outsider, this is probably a little confusing. Let’s start with a brief history of objects as a programming construct.

Storia degli oggetti
ahemmm... non la racconto, volendo si può leggerla di là. Per quel che serve per JavaScript object è qualsiasi variabile che non rientri in quei tipi più semplici visti precedentemente. Questo mi sembra dovrebbe essere sufficiente per proseguire.

This chapter describes JavaScript’s rather eccentric take on objects and the way they relate to some classical object-oriented techniques.

Metodi
Methods are simply properties that hold function values. This is a simple method (file rb.js):

var rabbit = {};
rabbit.speak = function(line) {
  console.log("The rabbit says '" + line + "'");
};

rabbit.speak("Ciao, eccomi!");

Usually a method needs to do something with the object it was called on. When a function is called as a method—looked up as a property and immediately called, as in object.method() —the special variable this in its body will point to the object that it was called on (rb1.js).

function speak(line) {
  console.log("The " + this.type + " rabbit says '" +
              line + "'");
}
var whiteRabbit = {type: "white", speak: speak};
var fatRabbit = {type: "fat", speak: speak};

whiteRabbit.speak("Oh my ears and whiskers, " +
                  "how late it's getting!");
fatRabbit.speak("I could sure use a carrot right now.");

The code uses the this keyword to output the type of rabbit that is speaking. Recall that the apply and bind methods both take a first argument that can be used to simulate method calls. This first argument is in fact used to give a value to this.

There is a method similar to apply, called call. It also calls the function it is a method of but takes its arguments normally, rather than as an array. Like apply and bind, call can be passed a specific this value (rb2.js).

var fatRabbit = {type: "fat", speak: speak};
function speak(line) {
  console.log("The " + this.type + " rabbit says '" +
              line + "'");
}              

speak.apply(fatRabbit, ["Burp!"]);
speak.call({type: "old"}, "Oh my.");

Prototipi
Watch closely (pr0.js).

var empty = {};
console.log(empty.toString);
console.log(empty.toString());

I just pulled a property out of an empty object. Magic!

Well, not really. I have simply been withholding information about the way JavaScript objects work. In addition to their set of properties, almost all objects also have a prototype. A prototype is another object that is used as a fallback source of properties. When an object gets a request for a property that it does not have, its prototype will be searched for the property, then the prototype’s prototype, and so on.

So who is the prototype of that empty object? It is the great ancestral prototype, the entity behind almost all objects, Object.prototype (pr1.js).

console.log(Object.getPrototypeOf({}) ==
            Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));

As you might expect, the Object.getPrototypeOf function returns the prototype of an object.

The prototype relations of JavaScript objects form a tree-shaped structure, and at the root of this structure sits Object.prototype. It provides a few methods that show up in all objects, such as toString, which converts an object to a string representation.

Many objects don’t directly have Object.prototype as their prototype, but instead have another object, which provides its own default properties. Functions derive from Function.prototype, and arrays derive from Array.prototype (pr2.js).

console.log(Object.getPrototypeOf(isNaN) ==
            Function.prototype);
console.log(Object.getPrototypeOf([]) ==
            Array.prototype);

Such a prototype object will itself have a prototype, often Object.prototype, so that it still indirectly provides methods like toString.

The Object.getPrototypeOf function obviously returns the prototype of an object. You can use Object.create to create an object with a specific prototype (prr.js).

var protoRabbit = {
  speak: function(line) {
    console.log("The " + this.type + " rabbit says '" +
                line + "'");
  }
};
var killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!");

The “proto” rabbit acts as a container for the properties that are shared by all rabbits. An individual rabbit object, like the killer rabbit, contains properties that apply only to itself—in this case its type—and derives shared properties from its prototype.

Eh sì, siamo arrivati al dunque, siamo javascripters, kwasy 😜
Continua, prossimamente 😎

:mrgreen:

JavaScript 30 – funzioni di ordine superiore – 6

Continuo da qui, copio qui.

Tutti e qualcuno
Arrays also come with the standard methods every and some. Both take a predicate function that, when called with an array element as argument, returns true or false. Just like && returns a true value only when the expressions on both sides are true, every returns true only when the predicate returns true for all elements of the array. Similarly, some returns true as soon as the predicate returns true for any of the elements. They do not process more elements than necessary—for example, if some finds that the predicate holds for the first element of the array, it will not look at the values after that.

Write two functions, every and some, that behave like these methods, except that they take the array as their first argument rather than being a method.

every.js

function every(arr, val) {
  var t = true;
  for (c = 0; c < arr.length; c++) {
    t = t && val(arr[c]);
    if (!t) break;
  }
  return t;
}

console.log(every([NaN, NaN, NaN], isNaN));
console.log(every([NaN, NaN, 4], isNaN));

some.js

function some(arr, val) {
  var t = false;
  for (c = 0; c < arr.length; c++) {
    t = val(arr[c]);
    if (t) break;
  }
  return t;
}

console.log(some([NaN, 3, 4], isNaN));
console.log(some([2, 3, 4], isNaN));

Sì, lo so che si potrebbero scrivere più brevi ma così mi sembrano più chiare; anche perché sono niubbo (assay) 😜 E in una vita precedente ho passato tanto (troppo) tempo con il debug 😜

:mrgreen:

JavaScript 29 – funzioni di ordine superiore – 5

Continuo da qui, copio qui.

Sempre esercizi, oggi…

Differenza d’età madre-figlio
Using the example data set from this chapter, compute the average age difference between mothers and children (the age of the mother when the child is born). You can use the average function defined earlier in this chapter.

Note that not all the mothers mentioned in the data are themselves present in the array. The byName object, which makes it easy to find a person’s object from their name, might be useful here.

function average(array) {
  function plus(a, b) { return a + b; }
  return array.reduce(plus) / array.length;
}

var byName = {};
ancestry.forEach(function(person) {
  byName[person.name] = person;
});

Il problema consiste nel caricare i dati del file JSON fornito da Marijn ed estrarre quelli che servono all’elaborazione. Si può fare ma non è semplice; in alternativa si può andare qui.
Questo è il codice di Marijn.

function average(array) {  
  function plus(a, b) { 
    return a + b; 
  }
  return array.reduce(plus) / array.length;
}

var byName = {};
ancestry.forEach(function(person) {
  byName[person.name] = person;
});

var differences = ancestry.filter(function(person) {
  return byName[person.mother] != null;
}).map(function(person) {
  return person.born - byName[person.mother].born;
});

Per farlo girare con NodeJS sono necessari i soliti aggiustamenti, usando i files disponibili qui.

Sono sempre più dell’idea che esercizi di questo tipo sono troppo specifici, oltre lo scopo della serie che è quella di introduzione a JavaScript 😡

:mrgreen:

JavaScript 28 – funzioni di ordine superiore – 4

Continuo da qui, copio qui.

Comincio con gli sercizi relativi al capitolo

Appiattire (flattening)
Use the reduce method in combination with the concat method to “flatten” an array of arrays into a single array that has all the elements of the input arrays.

var arrays = [[1, 2, 3], [4, 5], [6]];

Provo, ecco flat.js:

var arrays = [[1, 2, 3], [4, 5], [6]];

function flat(arr){
  return arr.reduce(function(a, b) {
    return a.concat(b);
  });
}

console.log(flat(arrays));

:mrgreen:

JavaScript 27 – funzioni di ordine superiore – 3

Continuo da qui, copio qui.

JSON
Higher-order functions that somehow apply a function to the elements of an array are widely used in JavaScript. The forEach method is the most primitive such function. There are a number of other variants available as methods on arrays. To familiarize ourselves with them, let’s play around with another data set.

A few years ago, someone crawled through a lot of archives and put together a book on the history of my family name (Haverbeke—meaning Oatbrook). I opened it hoping to find knights, pirates, and alchemists … but the book turns out to be mostly full of Flemish farmers. For my amusement, I extracted the information on my direct ancestors and put it into a computer-readable format.

The file I created looks something like this:

[
  {"name": "Emma de Milliano", "sex": "f",
   "born": 1876, "died": 1956,
   "father": "Petrus de Milliano",
   "mother": "Sophia van Damme"},
  {"name": "Carolus Haverbeke", "sex": "m",
   "born": 1832, "died": 1905,
   "father": "Carel Haverbeke",
   "mother": "Maria van Brussel"},
   ... and so on
]

This format is called JSON (pronounced “Jason”), which stands for JavaScript Object Notation. It is widely used as a data storage and communication format on the Web.

JSON is similar to JavaScript’s way of writing arrays and objects, with a few restrictions. All property names have to be surrounded by double quotes, and only simple data expressions are allowed—no function calls, variables, or anything that involves actual computation. Comments are not allowed in JSON.

JavaScript gives us functions, JSON.stringify and JSON.parse, that convert data to and from this format. The first takes a JavaScript value and returns a JSON-encoded string. The second takes such a string and converts it to the value it encodes (file JS0.js).

var string = JSON.stringify({name: "X", born: 1980});
console.log(string);
console.log(JSON.parse(string).born);

The variable ANCESTRY_FILE, available in the sandbox for this chapter and in a downloadable file on the website, contains the content of my JSON file as a string. Let’s decode it and see how many people it contains (JS1.js).

ANCESTRY_FILE = require('./ancestry.js');
// questo verrà spiegato prossimamente

var ancestry = JSON.parse(ANCESTRY_FILE);
console.log(ancestry.length);

Nota: modificato il codice; ho dovuto installare require.js da qui.

Filtrare un array
To find the people in the ancestry data set who were young in 1924, the following function might be helpful. It filters out the elements in an array that don’t pass a test (anc0.js).

ANCESTRY_FILE = require('./ancestry.js');
var ancestry = JSON.parse(ANCESTRY_FILE);

function filter(array, test) {
  var passed = [];
  for (var i = 0; i < array.length; i++) {
    if (test(array[i])) passed.push(array[i]); 
  } 
  return passed; 
} 
console.log(filter(ancestry, function(person) { 
  return person.born > 1900 && person.born < 1925;
}));

This uses the argument named test, a function value, to fill in a “gap” in the computation. The test function is called for each element, and its return value determines whether an element is included in the returned array.

Three people in the file were alive and young in 1924: my grandfather, grandmother, and great-aunt.

Note how the filter function, rather than deleting elements from the existing array, builds up a new array with only the elements that pass the test. This function is pure. It does not modify the array it is given.

Like forEach, filter is also a standard method on arrays. The example defined the function only in order to show what it does internally. From now on, we’ll use it like this instead:

function reduceAncestors(person, f, defaultValue) {
  function valueFor(person) {
    if (person == null)
      return defaultValue;
    else
      return f(person, valueFor(byName[person.mother]),
                       valueFor(byName[person.father]));
  }
  return valueFor(person);
}

The inner function (valueFor) handles a single person. Through the magic of recursion, it can simply call itself to handle the father and the mother of this person. The results, along with the person object itself, are passed to f, which returns the actual value for this person.

We can then use this to compute the amount of DNA my grandfather shared with Pauwels van Haverbeke and divide that by four.

function sharedDNA(person, fromMother, fromFather) {
  if (person.name == "Pauwels van Haverbeke")
    return 1;
  else
    return (fromMother + fromFather) / 2;
}
var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0) / 4);

Nota: Marijn ha distribuito su tanti files le funzioni che gli servono, richiamandoli con funzioni non ancora viste e senza usare NodeJS. Risulta quindi inutilmente complesso eseguirle in node 👿

The person with the name Pauwels van Haverbeke obviously shared 100 percent of his DNA with Pauwels van Haverbeke (there are no people who share names in the data set), so the function returns 1 for him. All other people share the average of the amounts that their parents share.

So, statistically speaking, I share about 0.05 percent of my DNA with this 16th-century person. It should be noted that this is only a statistical approximation, not an exact amount. It is a rather small number, but given how much genetic material we carry (about 3 billion base pairs), there’s still probably some aspect in the biological machine that is me that originates with Pauwels.

We could also have computed this number without relying on reduceAncestors. But separating the general approach (condensing a family tree) from the specific case (computing shared DNA) can improve the clarity of the code and allows us to reuse the abstract part of the program for other cases. For example, the following code finds the percentage of a person’s known ancestors who lived past 70 (by lineage, so people may be counted multiple times):

function countAncestors(person, test) {
  function combine(current, fromMother, fromFather) {
    var thisOneCounts = current != person && test(current);
    return fromMother + fromFather + (thisOneCounts ? 1 : 0);
  }
  return reduceAncestors(person, combine, 0);
}
function longLivingPercentage(person) {
  var all = countAncestors(person, function(person) {
    return true;
  });
  var longLiving = countAncestors(person, function(person) {
    return (person.died - person.born) >= 70;
  });
  return longLiving / all;
}
console.log(longLivingPercentage(byName["Emile Haverbeke"]));

Such numbers are not to be taken too seriously, given that our data set contains a rather arbitrary collection of people. But the code illustrates the fact that reduceAncestors gives us a useful piece of vocabulary for working with the family tree data structure.

Collegamenti
The bind method, which all functions have, creates a new function that will call the original function but with some of the arguments already fixed.

The following code shows an example of bind in use. It defines a function isInSet that tells us whether a person is in a given set of strings. To call filter in order to collect those person objects whose names are in a specific set, we can either write a function expression that makes a call to isInSet with our set as its first argument or partially apply the isInSet function.

var theSet = ["Carel Haverbeke", "Maria van Brussel",
              "Donald Duck"];
function isInSet(set, person) {
  return set.indexOf(person.name) > -1;
}

console.log(ancestry.filter(function(person) {
  return isInSet(theSet, person);
}));
// → [{name: "Maria van Brussel", …},
//    {name: "Carel Haverbeke", …}]
console.log(ancestry.filter(isInSet.bind(null, theSet)));

si può espandere “...” ottenendo

The call to bind returns a function that will call isInSet with theSet as first argument, followed by any remaining arguments given to the bound function.

The first argument, where the example passes null, is used for method calls, similar to the first argument to apply. I’ll describe this in more detail in the next chapter.

Marijn (rockz! 🚀) ed io abbiamo idee diverse su come devono essere fatti gli esempi 😡

:mrgreen:

JavaScript 26 – funzioni di ordine superiore – 2

Continuo da qui, copio qui.

Funzioni di ordine superiore (higher-order)
Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions. If you have already accepted the fact that functions are regular values, there is nothing particularly remarkable about the fact that such functions exist. The term comes from mathematics, where the distinction between functions and other values is taken more seriously.

Higher-order functions allow us to abstract over actions, not just values. They come in several forms. For example, you can have functions that create new functions (file ho0.js).

function greaterThan(n) {
  return function(m) { return m > n; };
}
var greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));

And you can have functions that change other functions (ho1.js).

function noisy(f) {
  return function(arg) {
    console.log("calling with", arg);
    var val = f(arg);
    console.log("called with", arg, "- got", val);
    return val;
  };
}
noisy(Boolean)(0);

You can even write functions that provide new types of control flow (ho2.js).

function unless(test, then) {
  if (!test) then();
}
function repeat(times, body) {
  for (var i = 0; i < times; i++) body(i);
}

repeat(3, function(n) {
  unless(n % 2, function() {
    console.log(n, "is even");
  });
});

The lexical scoping rules that we discussed  in Chapter 3  [nei post precedenti] work to our advantage when using functions in this way. In the previous example, the n variable is a parameter to the outer function. Because the inner function lives inside the environment of the outer one, it can use n. The bodies of such inner functions can access the variables around them. They can play a role similar to the {} blocks used in regular loops and conditional statements. An important difference is that variables declared inside inner functions do not end up in the environment of the outer function. And that is usually a good thing.

Passare i parametri
The noisy function defined earlier, which wraps its argument in another function, has a rather serious deficit.

function noisy(f) {
  return function(arg) {
    console.log("calling with", arg);
    var val = f(arg);
    console.log("called with", arg, "- got", val);
    return val;
  };
}
noisy(Boolean)(0);

If f takes more than one parameter, it gets only the first one. We could add a bunch of arguments to the inner function (arg1, arg2, and so on) and pass them all to f, but it is not clear how many would be enough. This solution would also deprive f of the information in arguments.length. Since we’d always pass the same number of arguments, it wouldn’t know how many arguments were originally given.

For these kinds of situations, JavaScript functions have an apply method. You pass it an array (or array-like object) of arguments, and it will call the function with those arguments.

function transparentWrapping(f) {
  return function() {
    return f.apply(null, arguments);
  };
}

x0

That’s a useless function, but it shows the pattern we are interested in—the function it returns passes all of the given arguments, and only those arguments, to f. It does this by passing its own arguments object to apply. The first argument to apply, for which we are passing null here, can be used to simulate a method call. We will come back to that in the next chapter.

Da approfondire, prossimamente sarà (quasi) tutto chiaro 😊

:mrgreen:

JavaScript 25 – funzioni di ordine superiore – 1

Continuo da qui, copio qui.

Nuovo capitolo che Marijn inizia con due cit. bellissimissime, da legere (là). Poi parte…

A large program is a costly program, and not just because of the time it takes to build. Size almost always involves complexity, and complexity confuses programmers. Confused programmers, in turn, tend to introduce mistakes (bugs) into programs. A large program also provides a lot of space for these bugs to hide, making them hard to find.

Let’s briefly go back to the final two example programs in the introduction. The first is self-contained and six lines long.

var total = 0, count = 1;
while (count <= 10) {
  total += count;
  count += 1;
}
console.log(total);

The second relies on two external functions and is one line long.

console.log(sum(range(1, 10)));

Which one is more likely to contain a bug?

If we count the size of the definitions of sum and range, the second program is also big—even bigger than the first. But still, I’d argue that it is more likely to be correct.

It is more likely to be correct because the solution is expressed in a vocabulary that corresponds to the problem being solved. Summing a range of numbers isn’t about loops and counters. It is about ranges and sums.

The definitions of this vocabulary (the functions sum and range) will still involve loops, counters, and other incidental details. But because they are expressing simpler concepts than the program as a whole, they are easier to get right.

Per chi inizia è difficile capire tutta l’importanza di queste poche righe. E non basta leggerle memorizzarle, si deve imparare a proprie spese, l’esperienza.

Astrazione
In the context of programming, these kinds of vocabularies are usually called abstractions. Abstractions hide details and give us the ability to talk about problems at a higher (or more abstract) level.

As an analogy, compare these two recipes for pea soup:

Put 1 cup of dried peas per person into a container. Add water until the peas are well covered. Leave the peas in water for at least 12 hours. Take the peas out of the water and put them in a cooking pan. Add 4 cups of water per person. Cover the pan and keep the peas simmering for two hours. Take half an onion per person. Cut it into pieces with a knife. Add it to the peas. Take a stalk of celery per person. Cut it into pieces with a knife. Add it to the peas. Take a carrot per person. Cut it into pieces. With a knife! Add it to the peas. Cook for 10 more minutes.

And the second recipe:

Per person: 1 cup dried split peas, half a chopped onion, a stalk of celery, and a carrot.

Soak peas for 12 hours. Simmer for 2 hours in 4 cups of water (per person). Chop and add vegetables. Cook for 10 more minutes.

The second is shorter and easier to interpret. But you do need to understand a few more cooking-related words—soak, simmer, chop, and, I guess, vegetable.

When programming, we can’t rely on all the words we need to be waiting for us in the dictionary. Thus, you might fall into the pattern of the first recipe—work out the precise steps the computer has to perform, one by one, blind to the higher-level concepts that they express.

It has to become second nature, for a programmer, to notice when a concept is begging to be abstracted into a new word.

Astrazione per l’attraversamento di un array
Plain functions, as we’ve seen them so far, are a good way to build abstractions. But sometimes they fall short.

In the previous chapter, this type of for loop made several appearances (file ar1.js):

var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
  var current = array[i];
  console.log(current);

It’s trying to say, “For each element in the array, log it to the console”. But it uses a roundabout way that involves a counter variable i, a check against the array’s length, and an extra variable declaration to pick out the current element. Apart from being a bit of an eyesore, this provides a lot of space for potential mistakes. We might accidentally reuse the i variable, misspell length as lenght, confuse the i and current variables, and so on.

So let’s try to abstract this into a function. Can you think of a way?

Well, it’s easy to write a function that goes over an array and calls console.log on every element (ar2.js).

function logEach(array) {
  for (var i = 0; i < array.length; i++)
    console.log(array[i]);
}

var arr = [1, 2, 3];
logEach(arr);

But what if we want to do something other than logging the elements? Since “doing something” can be represented as a function and functions are just values, we can pass our action as a function value (ar3.js).

function forEach(array, action) {
  for (var i = 0; i < array.length; i++)
    action(array[i]);
}

forEach(["Wampeter", "Foma", "Granfalloon"], console.log);

In some browsers, calling console.log in this way does not work. You can use alert instead of console.log if this example fails to work.

Often, you don’t pass a predefined function to forEach but create a function value on the spot instead (ar4.js).

var numbers = [1, 2, 3, 4, 5];
var sum = 0;
numbers.forEach(function(number) {
  sum += number;
});
console.log(sum);

Nota: ho modificato il codice di Marijn; probabilmente dovuto a qualche cambiamento intervenuto con il procedere delle versioni.

This looks quite a lot like the classical for loop, with its body written as a block below it. However, now the body is inside the function value, as well as inside the parentheses of the call to forEach. This is why it has to be closed with the closing brace and closing parenthesis.

Using this pattern, we can specify a variable name for the current element (number), rather than having to pick it out of the array manually.

In fact, we don’t need to write forEach ourselves. It is available as a standard method on arrays. Since the array is already provided as the thing the method acts on, forEach takes only one required argument: the function to be executed for each element.

To illustrate how helpful this is, let’s look back at a function from the previous chapter [qui]. It contains two array-traversing loops.

function gatherCorrelations(journal) {
  var phis = {};
  for (var entry = 0; entry < journal.length; entry++) {
    var events = journal[entry].events;
    for (var i = 0; i < events.length; i++) {
      var event = events[i];
      if (!(event in phis))
        phis[event] = phi(tableFor(event, journal));
    }
  }
  return phis;
}

Working with forEach makes it slightly shorter and quite a bit cleaner.

function gatherCorrelations(journal) {
  var phis = {};
  journal.forEach(function(entry) {
    entry.events.forEach(function(event) {
      if (!(event in phis))
        phis[event] = phi(tableFor(event, journal));
    });
  });
  return phis;
}

Nota: da verificare per la versione corrente.

:mrgreen:

JavaScript 24 – strutture di dati – oggetti e arrays – 9

Continuo da qui, copio qui.

Finisco con gli esercizi 😎

Comparazione spinta
The == operator compares objects by identity. But sometimes, you would prefer to compare the values of their actual properties.

Write a function, deepEqual, that takes two values and returns true only if they are the same value or are objects with the same properties whose values are also equal when compared with a recursive call to deepEqual.

To find out whether to compare two things by identity (use the === operator for that) or by looking at their properties, you can use the typeof operator. If it produces “object” for both values, you should do a deep comparison. But you have to take one silly exception into account: by a historical accident, typeof null also produces “object“.

Posso barare vero?
L’esercizio è di quelli non facili. Si può fare, certo, specie se si vedono i suggerimenti, Si tratta di controllare che siano entrambi oggetti non nulli e che siano uguali fino alla fine. Non metto la mia versione ma quella più chiara trovata su StackOverflow.

Non dico di copiare sempre da SO ma a volte… 😯

deepEqual

function deepEqual(a, b) {
  if (a === b) return true;

  if (a == null || typeof a != "object" ||
      b == null || typeof b != "object")
    return false;

  var propsInA = 0, propsInB = 0;

  for (var prop in a)
    propsInA += 1;

  for (var prop in b) {
    propsInB += 1;
    if (!(prop in a) || !deepEqual(a[prop], b[prop]))
      return false;
  }

  return propsInA == propsInB;
}

var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
console.log(deepEqual(obj, {here: 1, object: 2}));
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));

:mrgreen:

JavaScript 23 – strutture di dati – oggetti e arrays – 8

Continuo da qui, copio qui.

Sempre sugli esercizi 😎

Una lista
Objects, as generic blobs of values, can be used to build all sorts of data structures. A common data structure is the list (not to be confused with the array). A list is a nested set of objects, with the first object holding a reference to the second, the second to the third, and so on.

var list = {
  value: 1,
  rest: {
    value: 2,
    rest: {
      value: 3,
      rest: null
    }
  }
};

The resulting objects form a chain, like this:

A nice thing about lists is that they can share parts of their structure. For example, if I create two new values {value: 0, rest: list} and {value: -1, rest: list} (with list referring to the variable defined earlier), they are both independent lists, but they share the structure that makes up their last three elements. In addition, the original list is also still a valid three-element list (file ls0.js).

var list = {
  value: 1,
  rest: {
    value: 2,
    rest: {
      value: 3,
      rest: null
    }
  }
};

console.log('list (orig.):', list);

list = {value: 0, rest: list}
console.log('\nlist (con 0):', list);

list = {value: -1, rest: list}
console.log('\nlist (con -1):', list);

resta da scoprire perché non la visualizza tutta, prossimamente, forse 😯

Write a function arrayToList that builds up a data structure like the previous one when given [1, 2, 3] as argument, and write a listToArray function that produces an array from a list. Also write the helper functions prepend, which takes an element and a list and creates a new list that adds the element to the front of the input list, and nth, which takes a list and a number and returns the element at the given position in the list, or undefined when there is no such element.

If you haven’t already, also write a recursive version of nth.

arrayToList

function arrayToList(arr) {
    var list = {};
    
    var arev = arr.reverse();
    for (var c = 0; c < arev.length; c++) {
        list = {value: arev[c], rest: list};
    };
    return list;
}

array = ['A', 'B', 'C'];
console.log(array);
console.log(arrayToList(array));

Se non copio arr in arev l’originale viene sovrascritto globalmente; al solito dipende se serve.

listToArray

function listToArray(ls) {
    var arr = [];
    for (var node = ls; node; node = node.rest) {
        arr.push(node.value);
    }
    arr.pop(); // toglie il null finale
    return arr;
}

var lst = { value: 'A', 
            rest: { value: 'B', 
                    rest: { value: 'C', 
                            rest: {} } } }

console.log(listToArray(lst));

prepend

function prepend(ele, lst) {

    return {value: ele, rest: lst};
}

console.log(prepend(10, prepend(20, null)));

nth

function nth(list, n) {

    var ls = list;
    for (var c = 1; c < n; c++) {
        if (!ls) {
            return undefined;
            break;
        }
        ls = ls.rest;
    }
    return ls 
}

list = { value: 'A', 
            rest: { value: 'B', 
                    rest: { value: 'C', 
                            rest: {} } } };

console.log(list);
console.log(nth(list, 1));
console.log(nth(list, 2));
console.log(nth(list, 3));
console.log(nth(list, 10));

nth mi ha fatto sudare e –confesso– sono ricorso a StackOverflow per chiarimenti 😡

:mrgreen: