Why reference not kept when putting attribute on `this`? - javascript

Why the following code logs empty array, instead of the loaded array:
function Car() {
var parts = [];
this.parts = parts;
this.loadParts = loadParts;
function loadParts() {
parts = ['engine', 'wheels'];
}
}
var audi = new Car();
audi.loadParts();
console.log(audi.parts);
(Trying to implement the reveal pattern)

You're manipulating the closed-over local variable parts, not this.parts
Update your code to:
function loadParts() {
this.parts = ['engine', 'wheels'];
}
for more predictable results.

The problem is that your code reassign the local parts variable that it ends up pointing to a different array from this.parts.
A solution is to change the code to:
function loadParts() {
parts.splice(0, parts.length, 'engine', 'wheels');
}
this will mutate in-place the content of the same array instead.
In Javascript the code
var x = [1, 2, 3];
var y = x;
x = [4, 5, 6]
will not change the content of what y is pointing to.
This is what your code is doing (with y being this.parts).

Because you are overwriting the parts array with an entirely new array parts = ['engine', 'wheels'].
this.parts is a reference to the value of parts which is the original array parts = [];.
You either want to populate the original array:
function loadParts() {
parts.push('engine');
parts.push('wheels');
}
Or set the reference of this.parts to the new array
function loadParts() {
parts = ['engine', 'wheels'];
this.parts = parts;
}

when you assign a value to an Object/Array, it will contain the reference of the value, and it looses its own references just like what you did here :
this.parts = parts;
now when you did this :
function loadParts() {
parts = ['engine', 'wheels'];
}
parts loses its own reference and its no longer equal to this.parts, I suggest you just push data to the parts array
function loadParts() {
parts.push('engine', 'wheels');
}

Related

Adding two same rows in array than modifying only one of them

First, I am adding two same rows in array and later I need to modify only the last one, adding new property to it. The way I do that:
for(var index in arrayOne) {
var arrayOneItem = arrayOne[index];
var new_row = {
address: arrayOne[index].address,
date: arrayOne[index].date,
category: arrayOne[index].category,
};
rows.push(new_row);
if(arrayOne[index].refund_status == 'refunded') {
rows.push(new_row);
rows[rows.length - 1].refund_status = 'refunded';
}
}
But the problem is that the code inside if statement does not only modify last row, but also the one before it, so the refund_status = 'refunded' is added both to the last and one before last row. Why is this happening and is there a way to modify the last row only?
When you are using the same object twice it's best to create a copy (shallow in this case) using Object.assign(). This will avoid referencing the same object from multiple variables or array indexes in your case.
eg.
rows.push(new_row);
becomes
rows.push(Object.assign({}, new_row));
This is because the object you push into the array is passed by reference and not by value, thus when you change the original object you will change both references to it in the array, see example below:
let someArray = [];
let someObj = {foo: "bar"};
someArray.push(someObj);
someArray.push(someObj);
someArray[0].foo = "baz";
console.log(someArray[1]);
To avoid this, you would need to clone the values of the object to create a new one. This question has some ways to do so, using JSON.parse and JSON.stringify is the shortest way to deep-copy an object without an external library, see example below:
let someArray = [];
let someObj = {foo: "bar"};
someArray.push(someObj);
let newObj = JSON.parse(JSON.stringify(someObj));
someArray.push(newObj);
someArray[0].foo = "baz";
console.log(someArray[1]);
Because you are changing property of an object, and object in javaScript is accessed through a link not a separate instance. In another words, you have the same object in memory and you change its property. It means, new_row is object you create and push it several times, and it's the same.
You can avoid it by copying it when pushing second times:
if(arrayOne[index].refund_status == 'refunded') {
rows.push({ ...new_row });
rows[rows.length - 1].refund_status = 'refunded';
}
where { ...new_row } basically creates new copy.
When you do rows[rows.length - 1].refund_status = 'refunded'; only second last will change.
Another solution i'd suggest is a bit more accurate:
const rows = []; // empty
const arrayOne = []; // SOME DATA HERE as I understand
const refundedStatus = ;
arrayOne.forEach(element=> {
rows.push(element);
if (value.refund_status === 'refunded') {
rows[rows.length].refund_status = 'refunded';
rows.push({ ...element});
}
});

Javascript array concatenation

I have three variables a, b and c. I have to insert these into an array called arr. On calling function click, these variables should get pushed into arr array. eg. if i call click with arg 1,2 & 3 then array should be [1,2,3], now when I will pass the args 6,7 then the array should be [1,2,3,6,7].
In the code below, I tried to use concat method to get the desired result but i was unsuccessful.Is there any way around this?
function click(a,b,c){
var A=a;
var B=b;
var C=c;
var arr=[]
var newArr=[A,B,C]
arr.concat(newArr);
console.log(arr);
}
click(10,11,12);
click(12,13,14);
Declare a global array outside of the function and then after push values into it.
var arr = [];
function click(a,b,c){
arr.push(a, b, c)
}
click(10,11,12);
click(12,13,14);
console.log(arr);
The problem with your code is that, you are concatenating to the array but not considering the return value.
arr = arr.concat(newArr);
Then declare the array global to hold the values for every click.
function click(arr, a, b, c){
arr.push(a, b, c);
console.log(arr);
}
var arr = [];
click(arr, 10, 11, 12);
click(arr, 12, 13, 14);
If you don't want your arr outside your function, another approach is storing your arr inside click scope then call it anywhere (it doesn't pollute your global scope):
let click = (function (arr) {
return function (a,b,c) {
var A=a;
var B=b;
var C=c;
var newArr=[A,B,C]
arr = arr.concat(newArr);
console.log(arr);
}
})([])
click(10,11,12);
click(12,13,14);
First of all. You should either define arr outside your function or pass arr into function as a parameter.
Second one. Function concat returns concatenated arrays and left original arrays unchanged. So you need something like arr=arr.concat(newArr)
Destructuring assignment can be used to functionally concatenate
variables with arrays.
See below for a practical example.
// Input.
const input = [1, 2, 3]
// Click.
const click = (array, ...arguments) => [...array, ...arguments]
// Output.
const output = click(input, 4, 5, 6)
// Proof.
console.log(output)
Your Problem here is that the concat function returns a new array instance with the elements:
Check Here
function click(a,b,c){
var A=a;
var B=b;
var C=c;
var arr=[]
var newArr=[A,B,C]
arr = arr.concat(newArr);
// concat does not change the ref itself
console.log(arr);
}
click(10,11,12);
click(12,13,14);

removeDuplicates does not work as expected

I am trying to extend Array prototype with the following functions:
Array.prototype.uniqueItems=function(){
var ar=this,unique=[],max=ar.length;
for(var i = 0 ; i < max;i++)
if(unique.indexOf(ar[i] )==-1)
unique.push(ar[i]);
return unique;
};
Array.prototype.removeDuplicates=function(){
var self = this;
self = this.uniqueItems();
return self;
};
The first function is supposed to return an array without duplicates and the second to remove all duplicates from a given array.
Consider the following code:
var x=[1,1,1,1,2,2,2,2,3,3,3,3,4,4];
x.removeDuplicates();
console.log(x);
The output is :
[ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4 ]
So the duplicates still remain.Any ideas why?Thanks In advance!!!!Also notice that I can not use x= x.uniqueItems() since it will work only for x.I want it to work for any given array.
You might as well do it like this in ES6
var x = [1,1,1,1,2,2,2,2,3,3,3,3,4,4],
u = a => Array.from(new Set(a)),
y = u(x);
console.log(y);
The new Set object in ES6 takes elements and keeps only one of each and discard the dupes. So it's in a way ideal to get unique items. A new set object is instantitated by the Set constructor and it will accept an array of items as an argument such as in var s = new Set([1,1,2,1,4,3,6,7,4,5,6,9,8,1,2,3,4,7,0]) and the resulting s set will be composed of unique entries of 1,2,4,3,6,7,5,9,8 and 0 in the order of appearance. Set objects have generator properties and iterator functions which can be obtained from them. Hence [...s] (the ES6 spread operator) or Array.from(s) would result a proper unique items array automatically.
Converting the Array to a Set and back allows for an arguably cleaner implementation:
Array.prototype.uniqueItems = function() {
return new Array.from(new Set(this));
};
Array.prototype.removeDuplicates = function() {
this.from(new Set(this));
};
Reassign to x.
var x=[1,1,1,1,2,2,2,2,3,3,3,3,4,4];
x = x.removeDuplicates();
console.log(x);
when you say
self = this.uniqueItems();
self is now pointing to a new array, not this
You need to either do
x = x.removeDuplicates();
or iterate this again in removeDuplicates to remove the other items.
For example
Array.prototype.removeDuplicates=function(){
var self = this;
var that = this;
self = this.uniqueItems();
//remove all items
for(var counter = that.length ; counter >= 0; counter -- )
{
that.splice(counter,1);
}
//copy the individual items to this
Object.keys(self).forEach(function(index){
that[index] = self[index]
});
return self;
};
Hey thank you all for your answers .I found a simple solution to my problem by doing the following:
Array.prototype.removeDuplicates=function(){
//First create a copy of the current array without duplicates
var clear_array=this.uniqueItems();
//Delete every item in the current array
this.length=0;
//Fill the current array with the elements of clear_array
for(var i = 0;i<clear_array.length;i++)
this[i] = clear_array[i];
return this;
};
Also I did not use a Set since I want to be sure about backwards compatibility
Hope this will be helpful to someone :)

What is wrong with javascript?

I wrote some simple javascript code, but it does not return the expected result.
var arr = new Array();
var firstobj = {
cta: 0,
ccc: 0,
crc: 0
}
for (var ta = 1; ta <= 10; ta++) {
arr[ta] = firstobj;
}
arr[6].cta = 1;
console.log(arr);
I only change cta value of 6th element, but it changes for all elements.
Expected:
[{cta:0, ccc:0, crc:0}, ..., {cta:1, ccc:0, crc:0}, {cta:0, ccc:0, crc:0}, ...]
// ^ 6th element
Actual:
[{cta:1, ccc:0, crc:0, ...] // All have 1
Objects in Javascript are assigned by pointer (well technically JS doesn't have pointers, but it works like a pointer does in other languages). So, you have filled your array so that every element contains a pointer-like reference to the exact same object. Thus, when you change that object, you see the change everywhere because there's only one object and you changed it.
If you want to initialize an array with separate objects, you have to create a new object or clone an existing object and assign that separate object into each element of your array.
For example, you could do that like this:
var myArray = [];
var obj;
// Also, arrays start from 0, not from 1.
for (var i = 0; i < 10; i++) {
// create a new object for each item in the array
obj = {cta:0, ccc:0, crc:0};
myArray[i] = obj;
}
myArray[6].cta = 1;
document.write(JSON.stringify(myArray));
you assigned the same object reference to all values in the array
make it (use Object.create)
for (var ta = 1; ta <= 10; ta++)
{
arr [ta] = Object.create( firstobj );
}
When you save the same firstobj to your array it saved by reference so when you changing one of the values it changes all of them, my suggestion is to create a new object for each element in your array.
var arr = new Array();
for (var ta = 1; ta <= 10; ta++) {
arr [ta] = { cta:0,
ccc:0,
crc:0
};
}
arr[6].cta=1;
console.log(arr);
Only the 6th element's cta value must be 1, but all elements cta
values change.
all the elements in your array(apart from element at zero index , which would be undefined in your case) are referring to the same object so if you change property in one element it is refelected across all elements
Each element in array is referring to same object.
So when you change object value it is reflected in all elements.

Another javascript array challenge

Solving another array manipulation, and I'm taking longer than usual to solve this. I need help in combining array values:
var array1 = ["alpha|LJ", "bravo|MH", "charlie|MH", "delta|MF",
"echo|16", "{foxtrot}|GG", "{golf}|HS"];
var array2 = ["charlie-{golf}-{foxtrot}", "echo-{golf}"]; //some templates
such that the final array be:
final_array = ["alpha-LJ", "bravo-MH", "charlie-HS-GG-MH", "delta-MF",
"echo-HS-16"];
To make it clear how I arrived with the final_array, alpha, bravo and delta only got their "|" replaced with "-" since they are not found on my array2 template. charlie and echo got the template so the respective values of the {} were replaced based on array1. Array1 honestly is not the best key:value relationship that I could come up for now.
Here are some requirementL:
* Anything in array1 with {} braces are not meant to be templated.
* Keywords in array2 will always have a matching value in array1.
I've read about jquery .map() and thinking that it is achievable using this, maybe together with Regexp. Hope you'll utilize these. Also, if it helps, final_array can be of any order.
I really need to up my knowledge on these two topics... :|
Thank you in advance.
Edit: Updated to match your output and comment some of the madness. This doesn't feel like it's the most efficient, given the split() done to values at the start and then again at the end...but it works.
function funkyTransform( values, templates ){
// Make a copy of the array we were given so we can mutate it
// without rudely changing something passed to our function.
var result = values.concat();
// Map {value} entries for later lookup, and throw them out of the result
var valueMap = {};
for (var i=result.length-1;i>=0;--i){
var pair = result[i].split('|');
if (pair[0][0]=="{"){
valueMap[pair[0]] = pair[1];
result.splice(i,1); // Yank this from the result
}
}
console.log(valueMap);
// {
// "{foxtrot}": "GG",
// "{golf}": "HS"
// }
// Use the value map to replace text in our "templates", and
// create a map from the first part of the template to the rest.
// THIS SHOULD REALLY SCAN THE TEMPLATE FOR "{...}" PIECES
// AND LOOK THEM UP IN THE MAP; OOPS O(N^2)
var templateMap = {};
for (var i=templates.length-1;i>=0;--i){
var template = templates[i];
for (var name in valueMap){
if (valueMap.hasOwnProperty(name)){
template = template.replace(name,valueMap[name]);
}
}
var templateName = template.split('-')[0];
templateMap[ templateName ] = template.slice(templateName.length+1);
}
console.log(templateMap);
// {
// "charlie": "HS-GG",
// "echo": "HS"
// }
// Go through the results again, replacing template text from the templateMap
for (var i=result.length-1;i>=0;--i){
var pieces = result[i].split('|');
var template = templateMap[pieces[0]];
if (template) pieces.splice(1,0,template);
result[i] = pieces.join('-');
}
return result;
}
var output = funkyTransform( array1, array2 );
console.log(output);
// ["alpha-LJ", "bravo-MH", "charlie-HS-GG-MH", "delta-MF", "echo-HS-16"]
This managed to get your desired output, though I made a few assumptions:
Anything in array1 with {} braces are not meant to be templated.
Keywords in array2 will always have a matching value in array1 (this can easily be changed, but not sure what your rule would be).
Code:
// This is the main code
var final_array = $.map(array1, function (item) {
var components = item.split('|');
// Ignore elements between {} braces
if (/^\{.*\}$/.test(components[0])) return;
components[0] = template(components[0]);
return components.join('-');
});
// Helper to lookup array2 for a particular string and template it
// with the values from array1
function template(str) {
var index = indexOfMatching(array2, str, '-');
if (index == -1) return str;
var components = array2[index].split('-');
var result = [str];
for (var i = 1; i < components.length; i++) {
result.push(array1[indexOfMatching(array1, components[i], '|')]
.split('|')[1]);
}
return result.join('-');
}
// Helper to for looking up array1 and array2
function indexOfMatching(array, target, separator) {
for (var i = 0; i < array.length; i++) {
if (array[i].split(separator)[0] === target) return i;
}
return -1;
}

Categories

Resources