Why doesn't the below code log "onetwo" as expected? - javascript

We have the function
function Buffer(initValue) {
this.append(initValue);
}
Buffer.prototype = {
items: [],
append: function(str) {
this.items[this.items.length] = str instanceof Buffer ? str.toString() : str;
return this;
},
toString: function() {
return this.items.join("");
}
};
console.log(new Buffer("one").append(new Buffer("two")).toString());
but suddenly it logs "onetwoonetwo" - beyond all expectations... Why?

Several answers explain why this happens in your specific instance.
I suspect that what you really want is to let items be local to each instance of Buffer instead of shared via the prototype. This is simply done by declaring items = []; in the constructor:
function Buffer(initValue) {
this.items = []; // This line replaces `items: [],` in the original code.
this.append(initValue);
}
Buffer.prototype = {
append: function(str) {
this.items[this.items.length] = str instanceof Buffer ? str.toString() : str;
return this;
},
toString: function() {
return this.items.join("");
}
};

This is because you applied append multiple time.
Since you are using prototype, your item array is shared by your two instance of Buffer.
You should have this: http://jsbin.com/luhoki/3/watch?html,css,js,output
function Buffer(initValue) {
this.append(initValue);
}
Buffer.prototype = {
items: [],
append: function(str) {
this.items[this.items.length] = str instanceof Buffer ? str.toString() : str;
return this;
},
toString: function() {
return this.items.join("");
}
};
a = new Buffer("one")
a.append("two")
console.log(a.toString());
// result: "onetwo"

Here is why you get such a behavior.
When following line is encountered
console.log(new Buffer("one").append(new Buffer("two")).toString());
let's breakdown it in smaller for our convenience.
browser evaluates new Buffer("one").
then browser evaluates new Buffer("two")
finally above two results are evalueated like result1.append(result2).
So when first gets evaluated Item array will have one. Then after second will be evaluated then item array will have two element "one" & "two" since it is shared array.
Consider first result as result1 and second result as result2. Now when third step is executed it will join result1's item array which is having two elements and result2's item array which is same as result1's so you get four element in log as onetwoonetwo.

Related

Double question javascript functions with arrays

This is a double question because I can just post once every 90 minutes.
First I have to write a function that replaces a character of a string.
//====================== EXAMPLE ========================
var str = "I,Really,Like,Pizza";
characterRemover(str, ",");
"I Really Like Pizza"; // <====== EXPECTED OUTPUT
//=========================================================
And puts a space in place of the chosen character. I tried this but is not working.
function chracterRemover(str, cha){
var replaced = str.split('cha').join(' ');
return replaced;
}
It returns just the same string.
And the second thing is that I have to write a function that returns true if the data type introduced is an arrat and false for the rest.
//====================== EXAMPLE ========================
var one = { name: "antonello" };
false; // <====== EXPECTED OUTPUT
var two = ["name", "antonello"];
true; // <====== EXPECTED OUTPUT
var three = [[], [], {}, "antonello", 3, function() {}];
true; // <====== EXPECTED OUTPUT
//=========================================================
I've tried this.
function isArrayFun(array){
if {
typeof array = 'array';
return "Array";
} else {
return "Not an array"
}
}
But as well, it doesnt work.
I get this error:
Uncaught SyntaxError: Unexpected token '{'
I don't know why.
Thanks in advance for the help.
// First One
const str = "I,Really,Like,Pizza";
console.log(str.split(',').join(' '));
// Second One
function isArrayFun(array){
return Array.isArray(array);
}
const one = { name: "antonello" };
console.log(isArrayFun(one));
const two = ["name", "antonello"];
console.log(isArrayFun(two));
const three = [[], [], {}, "antonello", 3, function() {}];
console.log(isArrayFun(three));
Problem 1:
You quoted cha so it's a literal string, not the variable. Use
function characterRemover(str, cha) {
var replaced = str.split(cha).join(' ');
return replaced;
}
var str = "I,Really,Like,Pizza";
console.log(characterRemover(str, ","));
Problem 2:
typeof returns object for arrays. The way to tell if something is an array is by calling Array.isArray().
You also have syntax errors in your if statement. The condition has to be inside (), not after {.
function isArrayFun(array) {
if (Array.isArray(array)) {
return "Array";
} else {
return "Not an array"
}
}
var one = { name: "antonello" };
console.log(isArrayFun(one));
var two = ["name", "antonello"];
console.log(isArrayFun(two));
var three = [[], [], {}, "antonello", 3, function() {}];
console.log(isArrayFun(three));
First question.
The function name is different than the function you called
you should use .split(cha) and not 'cha'. split cha will actually split your string by the string you passed into that parameter. And 'cha' looks for the string 'cha'
Working example:
var str = "I,Really,Like,Pizza";
function chracterRemover(str, cha){
var replaced = str.split(cha).join(' ');
return replaced;
}
console.log(chracterRemover(str, ","));
You could also use a simple RegExp instead of using split and join and take the function to another level by making it globally useful via a 3rd parameter, in which you could define the replacement:
var str = "I,Really,Like,Pizza";
function chracterRemover(str, cha, chaTo){
var reg = new RegExp(cha, "g");
return str.replace(reg, chaTo);
}
console.log(chracterRemover(str, ",", " "));
console.log(chracterRemover(str, ",", "."));
Second question:
There is already a function like that
Array.isArray(value)
you can pass any type of data into that value, if it is an array it returns true
working example:
let type1 = [1,5,6];
let type2 = {a: 47};
let type3 = 5;
let type4 = "hello";
console.log(Array.isArray(type1))
console.log(Array.isArray(type2))
console.log(Array.isArray(type3))
console.log(Array.isArray(type4))

New JS Library, how to improve it?

I'm trying to improve on a small library I'm starting to write:
var syn=(function(){
var container = {};
return function(variable){
return{
val: container[variable],
is: function(value){
container[variable] = value;
}
};
};
})();
Currently it's capable of storing snippets of information using the syntax syn("a").is(42); and returning the value by using syn("a").val.
I'd like to be able to do two things with this library:
I'd like to be able to create new instances of the syn object, so I could create multiple "synapses". e.g., var SynapseA = new syn;
I'd like to reduce syn("a").val to just syn("a") so that if I refer to syn("a") without a property or method it returns the equivalent of syn("a").val, but I have no idea how this would be done?
Appreciate any help...
What you want is not possible, because it is in some way contradictory:
I'd like to reduce syn("a").val to just syn("a") so that if I refer to syn("a") without a property or method it returns the equivalent of syn("a").val
Assume this were possible, and the value stored for "a" is 42 as in your example, then both of the following expressions would need to return 42:
syn("a")
syn("a").val
But if the first returns 42, then the second can't work: 42 does not have a property val. If the second works, then the first will necessarily return an object with a val property, which obviously is not what 42 is. So this is a contradiction.
In my opinion, the closest you can get to what you want, is relying on the special valueOf method, which will kick in when you force coercion to a number (or boolean). However, for this to work, you must assume that the values you store, are indeed numbers.
Here is how it would work:
function Syn() { // constructor, so you can create more than 1 syn object
var container = {};
var f = function(variable){
return {
valueOf: function() { // special method
return container[variable];
},
val: container[variable],
assign: function(value){
container[variable] = value;
}
}
};
// Only include next line, if you REALLY need the returned
// object to be considered an instance of Syn:
Object.setPrototypeOf(f, Object.getPrototypeOf(this));
return f;
}
var syn = new Syn();
console.log(syn instanceof Syn); // true
syn("a").assign(42);
console.log(syn("a").val); // 42
console.log(+syn("a")); // 42, because `syn("a")` is coerced to number
NB: I renamed the method is to assign, as that seems more meaningful.
Similarly to valueOf, you could rely on toString, which does a similar thing when you coerce the return value of syn("a") to string:
function Syn() { // constructor, so you can create more than 1 syn object
var container = {};
var f = function(variable){
return {
toString: function() { // special method
return container[variable];
},
val: container[variable],
assign: function(value){
container[variable] = value;
}
}
};
// Only include next line, if you REALLY need the returned
// object to be considered an instance of Syn:
Object.setPrototypeOf(f, Object.getPrototypeOf(this));
return f;
}
var syn = new Syn();
console.log(syn instanceof Syn); // true
syn("a").assign("hello");
console.log(syn("a").val); // "hello"
console.log(''+syn("a")); // "hello", because `syn("a")` is coerced to string
This will do what you're asking I think:
var syn = function() {
this.container = {};
}
syn.prototype.is = function(index, value){
this.container[index] = value;
}
syn.prototype.val = function(index){
return this.container[index];
}
I can do the following:
var synapseA = new syn();
synapseA.is("a", 42);
console.log(synapseA.val("a"));
var synapseB = new syn();
synapseB.is("a", 30);
console.log(synapseB.val("a"));
synapseB.is("b", 20);
console.log(synapseB.val("b"));
And I get 42, 30 and 20 logged out. Is this what you're after?

using javascript to code the preorder traversal

var preorderTraversal = function(root) {
var array = [];
if(!(root == null)){
array.push(root.val) ;
preorderTraversal(root.left);
preorderTraversal(root.right);
}
return array;
};
The code failed in testing when the test case is [1,2], I only output [1], how to fix it?
The problem is that you're creating a new, separate array in each recursive call (and then discarding it, since you don't do anything with what the recursive calls return).
An alternative approach is to pass in an "accumulator" array acc, and pass it to every recursive call, so that all elements get added to a single array:
var preorderTraversal = function(root, acc = []) {
if(!!root){
acc.push(root.val);
if (root.left) preorderTraversal(root.left, acc);
if (root.right) preorderTraversal(root.right, acc);
}
return acc;
};
You might also be interested in traversing pre-ordered BST iteratively, instead of recursively:
var preorderTraversal = function(root) {
/**
* Algorithm:
* 1. Create an empty stack [];
* 2. Do while stack is not empty:
* 2.1. Pop an item from stack and add it to the 'result' array.
* 2.2. Push 'right child' of popped item to stack.
* 2.3. Push 'left child' of popped item to stack.
*/
if (root == null) {
return [];
}
const stack = [];
const result = [];
stack.push(root);
while(stack.length > 0) {
let current = stack.pop();
result.push(current.val);
if (current.right) stack.push(current.right);
if (current.left) stack.push(current.left);
}
return result;
};
array is a local variable then:
you put 1 on array with push
recursively go to others sides when you create again array = []
push 2
when you return to your top of recursion stack array still have only 1
maybe is better if you can send array as argument, and use returned array for modify the local one
You will need a helper function for inline printing for js; this helper function should call your actual preorder function.
Inside of your preorder function, you need to always keep updating the string "passed" (I explain the "" afterwards). If current node is empty, you should return the current string you have at the moment, otherwise it would wipe it off.
function doPreOrder(root, str) {
if(!root) {
return str;
}
if(!str) {
str = "";
}
if(root) {
str += root.val + ' ';
str = doPreOrder(root.left, str);
str = doPreOrder(root.right, str);
}
return str;
}
function preOrder(root) {
var x = doPreOrder(root);
console.log(x);
}
As you can see, we first need to use the function, and only pass root. We're passing an undefined var to it, and it will be the only time we will enter the str = "" code, and then from that point on, the str will update for each new data. At the end, you just console log that var.

Convert JSON to JavaScript Object of Type X

I know all about JSON.stringify or JSON.parse in the sense that one serializes an object and one deserializes the string back into an object. This is great!
However, I have the following situation:
var i = new MyMagicalObject();
var oi = JSON.parse(JSON.stringify(i));
console.log(i.numFields()); // this is fine
console.log(oi.numFields()); // this throws since Object has no method 'numFields'
Basically, I'd like to treat oi as an instance of "MyMagicalObject" since that's what it is.
I'm sure there's some magic about setting the prototype on oi or something, but I'm fairly new to JavaScript. Any help would be appreciated.
You can't "store" JavaScript functions in JSON strings.
The only data types that can be stored in JSON are:
Number
String
Boolean
Array
Object
null
(source)
Anything that isn't one of those types, gets ignored:
function Test(){
this.foo = function(){
return 'bar';
}
this.theAnswer = '42';
}
var t = new Test();
alert(t.foo());
alert(JSON.stringify(t))
Your problem could be easily solved by redesigning your MyMagicalObject class. Here is an example of JSON-friendly class:
function MyMagicalObject(props) {
this.props = props || {};
}
MyMagicalObject.prototype.get = function(key) {
return this.props[key];
};
MyMagicalObject.prototype.set = function(key, val) {
this.props[key] = val;
return this;
};
MyMagicalObject.prototype.toJSON = function() {
return this.props;
};
MyMagicalObject.prototype.numFields = function() {
return Object.keys(this.props).length;
};
This realization follows two rules:
It's constructor accepts JSON representation as a first argument.
It provides toJSON method to tell JS engine how to convert its instance to JSON.
Check the following example:
var obj = new MyMagicalObject();
obj.set('foo', 42).set('bar', 'baz');
alert(obj.numFields()); // 2
var str = JSON.stringify(obj);
var obj2 = new MyMagicalObject(JSON.parse(str));
alert(obj2.numFields()); // 2
You can create a new MyMagicalObject() and then overwrite its properties with the one from oi.
var t = new MyMagicalObject();
for(var k in oi) t[k]=oi[k];
That should do the trick. If you have a more complex object (with more than 1 dimension), search for a copy function that deep copies all properties.
Add oi.prototype = MyMagicalObject.prototype; after line 3.
or
create a new object and copy the properties:
var oi2 = new MyMagicalObject();
for (var p in oi) {
if (oi.hasOwnProperty(p)) {
oi2[p] = oi[p]
}
}
console.log(oi2.numFields());

Set of objects in javascript

I'd like to have a set of objects in Javascript. That is, a data structure that contains only unique objects.
Normally using properties is recommended, e.g. myset["key"] = true. However, I need the keys to be objects. I've read that Javascript casts property names to strings, so I guess I can't use myset[myobject] = true.
I could use an array, but I need something better than O(n) performance for adding, finding and removing items.
It needs to be able to tell objects apart by reference only, so given:
var a = {};
var b = {};
then both a and b should be able to be added, because they're separate objects.
Basically, I'm after something like C++'s std::set, that can store Javascript objects. Any ideas?
ES6 provides a native Set:
let s = new Set();
let a = {};
let b = {};
s.add(a);
console.log(s.has(a)); // true
console.log(s.has(b)); // false
Here's a mad suggestion ... key it on the result of JSON.stringify(object)
It's not possible for all objects, but if your object has a .toString() method implemented, it is:
var x = {toString: function(){ return 'foo'; }};
var y = {toString: function(){ return 'bar'; }};
var obj = {};
obj[x] = 'X';
obj[y] = 'Y';
console.log(obj);
// { foo: 'X', bar: 'Y' }
If you want to make this easier, make it a class:
function myObj(name){
this.name = name;
}
myObj.prototype.toString = function(){ return this.name; }
var obj = {};
obj[new myObj('foo')] = 'X';
obj[new myObj('bar')] = 'Y';
I'm answering my own question, but I came up with an alternative solution I thought was interesting and thought it would be useful to share it.
cwolves' answer gave me an idea. Providing an object's toString() method uniquely identifies the instance, properties of an object can be used to store a set of objects. Essentially, to store object x, you can use items[x.toString()] = x;. Note that the value is the object itself, so then the set of objects can be extracted by looking at all item's properties and dumping all the values in to an array.
Here's the class, which I call ObjectSet, in full. It requires objects are uniquely identified by their toString() method, which is OK for my purposes. add, remove and contains should all run in better than O(n) time - whatever javascript's property access efficiency is, which hopefully is either O(1) or O(n log n).
// Set of objects. Requires a .toString() overload to distinguish objects.
var ObjectSet = function ()
{
this.items = {};
this.item_count = 0;
};
ObjectSet.prototype.contains = function (x)
{
return this.items.hasOwnProperty(x.toString());
};
ObjectSet.prototype.add = function (x)
{
if (!this.contains(x))
{
this.items[x.toString()] = x;
this.item_count++;
}
return this;
};
ObjectSet.prototype.remove = function (x)
{
if (this.contains(x))
{
delete this.items[x.toString()];
this.item_count--;
}
return this;
};
ObjectSet.prototype.clear = function ()
{
this.items = {};
this.item_count = 0;
return this;
};
ObjectSet.prototype.isEmpty = function ()
{
return this.item_count === 0;
};
ObjectSet.prototype.count = function ()
{
return this.item_count;
};
ObjectSet.prototype.values = function ()
{
var i, ret = [];
for (i in this.items)
{
if (this.items.hasOwnProperty(i))
ret.push(this.items[i]);
}
return ret;
};
I used Map, solved my case
const objectsMap = new Map();
const placesName = [
{ place: "here", name: "stuff" },
{ place: "there", name: "morestuff" },
{ place: "there", name: "morestuff" },
];
placesName.forEach((object) => {
objectsMap.set(object.place, object);
});
console.log(objectsMap);
For what you're trying to do (sets of objects), there is no native Javascript implementation. You would have to implement this on your own. One way to do this would be to implement a hashing function for your objects. The backing data-type of the set would be an associative array, where the key of the array is the value you get from calling the object's hash function, and the value of the array is the object itself.
Of course, this doesn't address the issue that you highlighted, so you will need to take equality into account as well (implement an equals function perhaps)?
Instead of making the hash function a property of the object itself, you can have a standalone hash function that takes in an object as input and generates a hash value (presumably by iterating over its properties).
Using this method you should be able to get O(1) for insertion, searching, and removing (not counting the order of the hash function, which shouldn't be any worse than O(n), especially if you are iterating over its properties to create your hashed value).
ECMAScript6 Set should behave like that:
Standard: http://www.ecma-international.org/ecma-262/6.0/#sec-set-o-p-v-throw
Unofficial ES6 cheat sheet: https://github.com/lukehoban/es6features#map--set--weakmap--weakset
Working example on Firefox 32 (but not implemented in Chromium 37):
if (Set) {
var s = new Set()
var a = {}
var b = {}
var c = {}
s.add(a)
s.add(b)
s.add(b)
assert(s.size === 2)
assert(s.has(a))
assert(s.has(b))
assert(!s.has(c))
}
This is not surprising since {} != {}: equality compares object addresses by default.
A module that implements it for browsers without support: https://github.com/medikoo/es6-set
Javascript Set's don't do deep object comparison.
Using lodash, this is a unique array with deep object comparison:
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
Just typed this up, it's only briefly tested:
var Set = function Set()
{
var list = [];
var contains;
this.contains = contains = function(x) {
return list.indexOf(x) >= 0;
}
var put;
this.put = put = function(x) {
if (!contains(x))
list.push(x);
return this;
}
var remove;
this.remove = remove = function(x)
{
var idx = list.indexOf(x);
if (idx >= 0)
list.splice(idx,1);
return this;
}
var all;
this.all = all = function()
{
return list.concat();
}
return this;
}
It seems that the inner call of function works when prefixed with this.
Exemple:
var put;
this.put = put = function(x) {
if (!this.contains(x))
list.push(x);
return this;
}
Please use this code as a reference.
const fruits = [
{name: 'apple', price: 100},
{name: 'apple', price: 100},
{name: 'orange', price: 200},
{name: 'grapes', price: 300}
];
const hasFruitDuplicated = () => {
const duplicatedDeleteFruits = fruits.filter((fruit, index) =>
fruits.findIndex(item => item.name === fruit.name && item.price === fruit.price) === index
);
return duplicatedDeleteFruits;
};
Given an array of the following type:
Array<{ foo: T1, bar: T2 }>
You can build a corresponding dictionary of type:
{ [foo: T1]: Set<T2> }
The look-up for { foo: fooValue, bar: barValue } can be performed as follows:
if (fooValue in dictionary && dictionary[fooValue].has(barValue))
This way we can build what would be an ObjectSet<T1, T2>
.
If you now have three elements, you can build the following dictionary:
{ [foo: T1]: ObjectSet<T2, T3> }
and extend your ObjectSet to any number of properties by induction.
That is assuming your types can be used as index signatures.

Categories