Javascript array mixed with json not working as intended - javascript

I have this piece of code, but whenever I run findTimeSlots() it immediately messes up my global array apptData but I don't see in anywhere in this function that is supposed to have changed it.
(ConsoleLog)
(index):69
(4) [{…}, {…}, {…}, {…}]
0: {service: "A, B", duration: 50, tech: "C"}
1: {service: "B", duration: 30, tech: "C"}
2: {service: "A, D", duration: 60, tech: "A"}
3: {service: "D", duration: 40, tech: "A"}
length: 4
__proto__: Array(0)
(index):45
(4) [{…}, {…}, {…}, {…}]
0: {service: "A, B", duration: 50, tech: "C"}
1: {service: "B", duration: 30, tech: "C"}
2: {service: "A, D", duration: 60, tech: "A"}
3: {service: "D", duration: 40, tech: "A"}
length: 4
__proto__: Array(0)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var apptData = [];
function addApptData(serviceName, rawDuration, selectedTech){
apptData.push({
'service': serviceName,
'duration': rawDuration,
'tech' : selectedTech
})
}
function reduceApptData(index){
apptData.splice(index, 1);
}
function findTimeSlots(dateStr){
console.log(apptData); //* Index 45 *//
var final = new Array();
var service, duration;
for(var i = 0; i < apptData.length; i++){
var duplicated = false;
for(var j = 0; j < final.length; j++){
if(final[j].tech == apptData[i].tech){
service = ", "+apptData[i].service;
final[j].service += service;
duration = apptData[i].duration;
final[j].duration += duration;
duplicated = true;
break;
}
}
if(!duplicated){
final.push(apptData[i]);
}
}
}
addApptData("A", 20, "C");
addApptData("B", 30, "C");
addApptData("A", 20, "A");
addApptData("D", 40, "A");
console.log(apptData); //* Index 69 *//
// If I remove the line below, I get the expected result, when I try to
// run with this line it will mess up apptData[]
findTimeSlots("");
What I expected to be is
0: {service: "A", duration: 20, tech: "C"}
1: {service: "B", duration: 30, tech: "C"}
2: {service: "A", duration: 20, tech: "A"}
3: {service: "D", duration: 40, tech: "A"}
length: 4
__proto__: Array(0)
So basically I expect it to remain the same.
I wanted to consolidate my var apptData into my var final inside my findTimeSlots()
After debugging, I found out that my apptData keep changing unexpectedly for some reason.
I suspect this to be very trivial but I cannot for the life of me figure out.

What is happening is explained in this tiny example:
let a = {test: 10};
let b = a;
b.text = 11;
console.log(a); //this will print {test: 11}
Since a is an object, when you assign let b = a; you are actually saving a reference to a, not cloning a.
You need to clone the object apptData[i], and to do that See this answer
So instead of final.push(apptData[i]); you should push a cloned apptData[i].
Hope that helps.

You should make a shallow copy of apptData.
One thing you should take note of JavaScript is the difference between assignment by value vs assingment by reference.
When primitive data types such as numbers(i.e. 2, 23), strings (i.e 'aaa', '123') are assigned to a variable, the variable contains the primitive value.
On the other hand, when non-primitive data types (arrays, objects, functions) are assigned to a variable, the variable contains the reference (which 'points' to the object's location on the memory), rather than the value itself.
To answer your question, you should be making a shallow copy of apptData, rather than making a direct reference to it.
Assuming that your apptData is an array of (non-nested) objects, here is a simply way to make a shallow copy of it using ES6's spread syntax:
const apptDataCopy = apptData.map(serviceObject => ({...serviceObject}));
From there, you can make any changes you wish to apptDataCopy, and it will not affect the original apptData.

var apptData = [], typeSelectedTech = new Set(), finalData =[], tmpObj = {serviceName: '', rawDuration: 0}
const addApptData = (serviceName, rawDuration, selectedTech)=> {
apptData.push({
serviceName,
rawDuration,
selectedTech
})
if (typeSelectedTech.has(selectedTech)) {
finalData.push({serviceName: tmpObj.selectedTech + ',' + serviceName, rawDuration: tmpObj.rawDuration + rawDuration, selectedTech})
}else {
tmpObj = {
serviceName,
rawDuration,
selectedTech
}
typeSelectedTech.add(selectedTech)
}
}
addApptData("A", 20, "C");
addApptData("B", 30, "C");
addApptData("A", 20, "A");
addApptData("D", 40, "A");
console.log(finalData);
console.log(apptData);
First of all, I believe that you have solved the problem, my answer is only for communication.
I tried the rewriting method, you can get the raw data and the target data. Although it produces multiple global variables, I can't think of other ways.
So as a communication, I hope other people can have a better way.

Related

How to turn object with array into array?

I'm trying to turn the values of an array inside an object to a normal array.
Right now I'm doing something like this in my AJAX success function:
var obj = data
console.log(obj)
var array = Object.keys(obj)
.map(function(key) {
return obj[key];
});
console.log(array)
This is what I get in my console:
{jaar: Array(2)}
jaar: Array(2)
0: YEAR(Scan): "2020"
[[Prototype]]: Object
1: YEAR(Scan): "2021"
[[Prototype]]: Object
length: 2
[[Prototype]]: Array(0)
[[Prototype]]: Object
I want to have something simply as this
["2020", "2021"]
How can I achieve this and what am I doing wrong right now?
console.log(obj) at the start gives this output in the console:
jaar: Array(2)
0: {YEAR(Scan): '2020'}
1: {YEAR(Scan): '2021'}
length: 2
[[Prototype]]: Array(0)
[[Prototype]]: Object ```
If you have a plain object like const obj = { x: 10, y: 20 } and you want to have an array of its values, you can simply use Object.values(obj) that will return you [10, 20].
Seems like your object has more complex structure.
If you know for sure, any value of your object is a value or array, like
const obj2 = { x: 10, y: [20, 30, 40], z: [50, 60]} you can flatten it.
Object.values(obj2).flat() will return you [10, 20, 30, 40, 50, 60].
For more complex structures you need to have more complex handlers.
jaar is already an array of objects. You can simply use .map() and get the respective property value from each array element.
let ans =jaar.map(x => x['YEAR(Scan)']);
Other answer provides the must succinct code (using .map) - to make the the smallest change to your existing code, change:
You can read the YEAR(Scan) property like so:
return obj[key]["YEAR(Scan)"];
Updated code:
var obj =
[
{ "YEAR(Scan)":"2020" },
{ "YEAR(Scan)":"2021" }
]
console.log(obj)
var array = Object.keys(obj)
.map(function(key) {
return obj[key]["YEAR(Scan)"];
});
console.log(array)

How to add object properties together (number)?

Hey so I'm working on a JS project, and I came across an issue where I am trying to add/merge 2 objects together. So basically, there is a base object:
{age: 0,
lvl: 123,
xp: 321}
So we have this, and I have another object coming in with
{age: 12,
lvl: 21}
The result I want is
{age: 12,
lvl: 144,
xp: 321}
But that could be easily achieved with just individual property addition. However, I want to come to a point where I don't know what properties the object has, yet they are still added. Oh and the property type will for sure be a number. Any ideas?
Edit:
Ok, I see I mis worded some stuff. What I meant by me not knowing which properties it has, I meant that I know that the object may have one-all properties of the first object, just that I don't know which ones the second one has and does have.
Loop through the keys of the second object and add them to the first:
const first = {
age: 0,
lvl: 123,
xp: 321
};
const second = {
age: 12,
lvl: 21
};
for (const key in second) {
first[key] = (first[key] || 0) + second[key];
}
console.log(first);
Read more about for...in loops here.
Write a function that makes a copy of the first object and adds the keys in:
function addProperties(firstObj, secondObj) {
const newObj = Object.assign({}, firstObj);
for (let key of Object.keys(secondObj)) {
if (newObj.hasOwnProperty(key)) {
newObj[key] += secondObj[key];
} else {
newObj[key] = secondObj[key];
}
}
return newObj;
}
const first = {
age: 0,
lvl: 123,
xp: 321
};
const second = {
age: 12,
lvl: 21
};
const result = addProperties(first, second);
console.log(result);

How map() and fill() work combined on a newly being created array

I have encountered similar code:
function createObj() {
return {
x: 'type1',
y: 100 };
}
const newArray = Array(4).fill("Dummy").map(createObj);
console.log(newArray);
This creates a new Array with 4 elements like below:
(4) [{…}, {…}, {…}, {…}]
0: {x: "type1", y: 100}
1: {x: "type1", y: 100}
2: {x: "type1", y: 100}
3: {x: "type1", y: 100}
length: 4
__proto__: Array(0)
I am not sure why it doesn't get populated with value I provided in fill ("Dummy").
I want to understand how this actually works.
I tweaked the code to this:
function createObj() {
return {
x: 'type1',
y: 100 };
}
const newArray = Array(4).fill("Dummy");
newArray.map(createObj);
console.log(newArray);
now the output is:
(4) ["Dummy", "Dummy", "Dummy", "Dummy"]
0: "Dummy"
1: "Dummy"
2: "Dummy"
3: "Dummy"
length: 4
__proto__: Array(0)
You can break the line of code down:
Array(4) // [empty, empty, empty, empty]
The above will create an array with four "empty" slots within it:
.fill("Dummy") // ["Dummy", "Dummy", "Dummy", "Dummy"]
For every empty slot within the array (from index 0 to the length of the array), the fill method will place the string "Dummy" in its location (as .fill will set all elements with a particular object from 0 to the length of the array). This is needed for the .map method to work (as .map() only iterates over values which exist).
.map(createObj)
will iterate through every element in your array, and convert it to a reference of the object returned by createObj. If you try and perform .map on an empty array, it will skip all empty slots, hence the need for .fill(). So, by using .map you are creating unique objects at each index.
They key difference is that map() returns an array rather than changing the original array.
In your first example const newArray = Array(4).fill("Dummy").map(createObj); the final call to map() returns a new array and assigns it to newArray.
In the second example const newArray = Array(4).fill("Dummy"); the reult of the fill() call is assigned to newArray. When map() is called, it runs, but the result is not assigned to any variable.
function createObj() {
return {
x: 'type1',
y: 100 };
}
const newArray = Array(4).fill("Dummy");
// This piece of code creates an array of length 4 filled with the text 'Dummy' in every index.
newArray.map(createObj);
console.log(newArray); // This console.log will point to newArray constant which is 3 rows above, it will not be the output of this: newArray.map(createObj)
const newThing = newArray.map(createObj);
// newThing is now what .map() does, which is to return a newly created array out of what you are passing.
console.log(newThing)
This is because you are returning an object - from the createObj function.
Remember the the map function returns a new array and the values within are the returned ones. Your Dummy data won't be useful in this case, only the size of the array.
let see the code piece by piece.
// will return [empty x 4]
Array(4);
// This is gonna fill in the array with the 'Dummy' text
// ['Dummy', 'Dummy', 'Dummy', 'Dummy']
Array(4).fill('Dummy');
This is what so understood so far when tweaked the code.
Now the map. If you return the value from the filled array you will get a new array fill in with the returned values
// the output is:
// ['Dummy', 'Dummy', 'Dummy', 'Dummy']
Array(4).fill('Dummy').map(value => value);
// BUT! remember what I wrote above about returning a // new value array fill in with the returned values.
// And in this case you are working ON THE RETURNED ARRAY FROM THE FILL FUNCTION.
//
// output:
// 0: {x: "type1", y: 100}
// 1: {x: "type1", y: 100}
// 2: {x: "type1", y: 100}
// 3: {x: "type1", y: 100}
Array(4).fill('Dummy').map(()=> ({
x: 'type1',
y: 100,
}));
Just remember that some functions like Array, fill, filter, map, flat, flatMap, reduce among others, they return a new array. So when you concat each function you are working on the returned array.
When we iterate over the map object it returns a new array (Immutable Function)
function createObj() {
return {
x: 'type1',
y: 100 };
}
const newArray = Array(4).fill("Dummy");
const result = newArray.map(createObj); //here return a new Array
console.log(result);

Reducing Javascript sub array to single array

I have a result of the following code
d3.csv("../hello.csv", function(data) {
console.log(data);
var usable = data.map(function (d) {
return {
description: d.description.split(" ")
};
});
console.log(usable);
});
the console log of the usable is [object, object, object] when I expand it is
0:Object
description: Array[3]
0 : "A"
1 : "B"
2 : "C"
1:Object
description: Array[3]
0 : "D"
1 : "E"
2 : "FG"
2:Object
description: Array[5]
0 : "AD"
1 : "BD"
2 : "DC"
4 : "HH"
What I need is single array as follows:
[A,B,C,D,E,FG,AD,BD,DC,HH]
with all elements reduced to single array rather than having multiple.
How can I achieve this? Any help would be highly appreciated.
You can flatten the array using D3.merge(). Assume these are your objects:
var obj1 = {description: [1,2,3]}
var obj2 = {description: [10,20,30,40]};
var obj3 = {description: [100,200,300,400,500]};
var array = [obj1, obj2, obj3]; // your array
So this produces the output you described:
console.log("array", array);
You can use d3.merge(array) to merge several arrays into one:
var flattened = d3.merge([obj1.description, obj2.description, obj3.description]);
console.log("flattened", flattened);
It will print:
flattened [1, 2, 3, 10, 20, 30, 40, 100, 200, 300, 400, 500]
See: JSFiddle
Why not:
d3.csv("../hello.csv", function(data) {
console.log(data);
var all = [];
data.map(function (d) {
Array.prototype.push.apply(all, d.description.split(" "));
});
console.log(all);
});

JQuery $.extend() recursivity peculiarity

I want to extend an object in a specific position in an array, without having to send the whole array again.
Example:
You start out with the array like this:
sensors = [{value: 10}, {value: 20}];
Now say I want to change the value of the second sensor, I can do this in jQuery:
newSensorValue = {1: {value:30}};
$.extend(true, sensors, newSensorValue);
// sensors = [{value: 10}, {value: 30}];
However, if the array is not directly the object that is merged, this does not work:
node = {x: 10, y: 10, node_id: 1, sensors: [
{sensor_id: 1, value: 10},
{sensor_id: 2, value: 20}
]};
newNodeData = {sensors: {
1: {value:30}
}};
$.extend(true, node, newNodeData);
// node = {x: 10, y: 10, node_id: 1, sensors: {
// 1: {value:30}
// }};
Why does $.extend() suddenly behave differently if the array is nested in an object?
My own solution would be to create an array of empty objects, only giving the object that I want to change the "value" attribute, like:
newNodeData = {sensors: [
{}, {value: 30}
]};
Since this seems rather ugly, is there a better way to do this?
I would prefer to keep the sensors attribute an array, since I like to use forEach to go through each sensor quickly.
EDIT: forgot to mention that if I do
$.extend(true, node.sensors, newNodeData.sensors);
It works the same as in the first example (i.e. it works)
You could leave the element empty when you don't want to change it.
node = {x: 10, y: 10, node_id: 1, sensors: [
{sensor_id: 1, value: 10},
{sensor_id: 2, value: 20}
]};
newNodeData = {sensors: [,{value:30}]};
$.extend(true, node, newNodeData);

Categories