I am working on an Angular 9, RxJS 6 app and have a question regarding the different outcomes of piping subject values and doing unit conversion in that pipe.
Please have a look at this stackblitz. There, inside the backend.service.ts file, an observable is created that does some "unit conversion" and returns everything that is emmitted to the _commodities Subject. If you look at the convertCommodityUnits function, please notice that I commented out the working example and instead have the way I solved it initially.
My question: When you use the unsubscribe buttons on the screen and subscribe again, when using the "conversion solution" that just overrides the object without making a copy, the values in the HTML are converted multiple times, so the pipe does not use the original data that the subject provides. If you use the other code, so creating a clone of the commodity object inside convertCommodityUnits, it works like expected.
Now, I don't understand why the two ways of converting the data behave so differently. I get that one manipulates the data directly, because js does Call by sharing and one returns a new object. But the object that is passed to the convertCommodityUnits function is created by the array.prototype.map function, so it should not overwrite anything, right? I expect that RxJS uses the original, last data that was emitted to the subject to pass into the pipe/map operators, but that does not seem to be the case in the example, right?
How/Why are the values converted multiple times here?
This is more or less a follow-up question on this: Angular/RxJS update piped subject manually (even if no data changed), "unit conversion in rxjs pipe", so it's the same setup.
When you're using map you got a new reference for the array. But you don't get new objects in the newly generated array (shallow copy of the array), so you're mutating the data inside the element.
In the destructuring solution, because you have only primitive types in each object in the array, you kind of generate completely brand new elements to your array each time the conversion method is called (this is important: not only a new array but also new elements in the array => you have performed a deep copy of the array). So you don't accumulate successively the values in each subscription.
It doesn't mean that the 1-level destructuring solution like you used in the provided stackblitz demo will work in all cases. I've seen this mistake being made a lot out there, particularly in redux pattern frameworks that need you to not mutate the stored data, like ngrx, ngxs etc. If you had complex objects in your array, the 1-level destructuring would've kept untouched all the embedded objects in each element of the array. I think it's easier to describe this behavior with examples:
const obj1 = {a: 1};
const array = [{b: 2, obj: obj1}];
// after every newArray assignment in the below code,
// console.log(newArray === array) prints false to the console
let newArray = [...array];
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => item);
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => ({...item}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // true
newArray = array.map(item => ({
...item,
obj: {...item.obj}
}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // false
i have a JSON data from AJAX response as below
{
"2015001":{"sname":"name1","01-07-2015":null,"02-07-2015":"0","03-07-2015":"0","04-07-2015":"0","05-07-2015":null,"06-07-2015":"0","07-07-2015":"0","08-07-2015":"0","09-07-2015":"0","10-07-2015":"0","11-07-2015":null,"12-07-2015":null,"13-07-2015":"0","14-07-2015":"1","15-07-2015":null,"16-07-2015":"2","17-07-2015":null,"18-07-2015":null,"19-07-2015":null,"20-07-2015":"2","21-07-2015":"0","22-07-2015":"0","23-07-2015":"0","24-07-2015":"1","25-07-2015":"1","26-07-2015":null,"27-07-2015":"2","28-07-2015":null,"29-07-2015":"2","30-07-2015":"2","31-07-2015":"2"},
"2015002":{"sname":"name2","01-07-2015":null,"02-07-2015":"0","03-07-2015":"1","04-07-2015":"1","05-07-2015":null,"06-07-2015":"0","07-07-2015":"0","08-07-2015":"0","09-07-2015":"0","10-07-2015":"2","11-07-2015":null,"12-07-2015":null,"13-07-2015":"1","14-07-2015":"1","15-07-2015":null,"16-07-2015":"2","17-07-2015":null,"18-07-2015":null,"19-07-2015":null,"20-07-2015":"0","21-07-2015":"0","22-07-2015":"0","23-07-2015":"0","24-07-2015":"0","25-07-2015":"0","26-07-2015":null,"27-07-2015":"0","28-07-2015":null,"29-07-2015":"0","30-07-2015":"0","31-07-2015":"0"},
"2015003":{"sname":"name3","01-07-2015":null,"02-07-2015":"2","03-07-2015":"2","04-07-2015":"2","05-07-2015":null,"06-07-2015":"2","07-07-2015":"2","08-07-2015":"0","09-07-2015":"2","10-07-2015":"2","11-07-2015":null,"12-07-2015":null,"13-07-2015":"2","14-07-2015":"0","15-07-2015":null,"16-07-2015":"2","17-07-2015":null,"18-07-2015":null,"19-07-2015":null,"20-07-2015":"2","21-07-2015":"2","22-07-2015":"0","23-07-2015":"2","24-07-2015":"2","25-07-2015":"2","26-07-2015":null,"27-07-2015":"2","28-07-2015":null,"29-07-2015":"2","30-07-2015":"2","31-07-2015":"2"},
"2015004":{"sname":"name4","01-07-2015":null,"02-07-2015":"2","03-07-2015":"2","04-07-2015":"2","05-07-2015":null,"06-07-2015":"0","07-07-2015":"2","08-07-2015":"2","09-07-2015":"2","10-07-2015":"2","11-07-2015":null,"12-07-2015":null,"13-07-2015":"2","14-07-2015":"2","15-07-2015":null,"16-07-2015":"2","17-07-2015":null,"18-07-2015":null,"19-07-2015":null,"20-07-2015":"0","21-07-2015":"2","22-07-2015":"2","23-07-2015":"2","24-07-2015":"2","25-07-2015":"0","26-07-2015":null,"27-07-2015":"2","28-07-2015":null,"29-07-2015":"0","30-07-2015":"2","31-07-2015":"2"},
"2015005":{"sname":"name5","01-07-2015":null,"02-07-2015":"2","03-07-2015":"2","04-07-2015":"0","05-07-2015":null,"06-07-2015":"2","07-07-2015":"2","08-07-2015":"2","09-07-2015":"2","10-07-2015":"2","11-07-2015":null,"12-07-2015":null,"13-07-2015":"2","14-07-2015":"2","15-07-2015":null,"16-07-2015":"2","17-07-2015":null,"18-07-2015":null,"19-07-2015":null,"20-07-2015":"0","21-07-2015":"2","22-07-2015":"2","23-07-2015":"2","24-07-2015":"2","25-07-2015":"2","26-07-2015":null,"27-07-2015":"2","28-07-2015":null,"29-07-2015":"2","30-07-2015":"2","31-07-2015":"2"},
"2015006":{"sname":"name6","01-07-2015":null,"02-07-2015":"2","03-07-2015":"2","04-07-2015":"2","05-07-2015":null,"06-07-2015":"2","07-07-2015":"2","08-07-2015":"2","09-07-2015":"2","10-07-2015":"2","11-07-2015":null,"12-07-2015":null,"13-07-2015":"2","14-07-2015":"2","15-07-2015":null,"16-07-2015":"2","17-07-2015":null,"18-07-2015":null,"19-07-2015":null,"20-07-2015":"2","21-07-2015":"0","22-07-2015":"2","23-07-2015":"2","24-07-2015":"2","25-07-2015":"2","26-07-2015":null,"27-07-2015":"2","28-07-2015":null,"29-07-2015":"2","30-07-2015":"2","31-07-2015":"2"}
}
each object has same number of objects. Here i want to count the number of objects in the first object(2015001). The key will change upon every request.
i tried
console.log(Object.keys(data[2015001]).length)
and i got what i need
but how can i do without key(2015001)
If you always want to know the number of keys of the first object, then you should use Object.keys(data[Object.keys(data)[0]]).length
Note that if the order is important to you, you should send the keys inside an array and not inside an object, as according to the specification, an object is an unordered set of name/value pairs.
You could get the first key and use it to access the object, probably the fastest way, but then again your information is a bit unclear, you are talking about objects and more objects but I am unsure when you talk about the outer object or when about the inner object.
for (var k in Object) {
break
}
console.log(Object.keys(data[k]).length)
I am very new to JavaScript and I am trying to figure out how to set a function to remove the last key:value pair to the right, much like array.pop for an array. This is an assignment I am working on. It seems confusing to me because, from my limited understanding of JS their is no specific order in a list of properties in an object. If anyone has any insight I would appreciate it. Here is the object:
var array = {length:0, size:big, smell:strange};
this is where I have started to go, but just having a hard time completing the function:
array.pop = function() {
//...
};
Ultimately I would like it to turn out like this:
array = {length:0, size:big};
Thanks in advance.
Objects do not have any defined order of properties so there is no "last" property. You have to remove a property by name, not position.
You can, of course, iterate over the properties and inspect them as you iterate and decide whether you want to delete any given property by looking at its name. Some javascript implementations will preserve the order that properties were added, but that is specifically not guaranteed by the ECMAScript specification so it cannot be relied upon.
This will work
const car = {
color: 'blue',
brand: 'Ford'
}
let keys = Object.keys(car)
delete car[keys[keys.length-1]]
console.log(car)
This answer is good for those situtations where the key is dynamically generated numbers like 0,1,2,3,4 etc
const myObject = {
0: 'somestring',
1: 42,
2: false
};
delete myObject[`${Object.keys(myObject).length-1}`]
console.log(myObject);
output:
Object { 0: "somestring", 1: 42 }
this one line logic may not good when key is a string. So, carefully use it.
The snippet below demonstrates that "objects have no order", and an [inefficient] workaround: use an array alongside of the object, to store the order that the properties were added to the object.
Click to add random properties, and note the order that they appear below.
In CodePen (or on my webserver) the properties seem to be stored sorted numerically (even though they're stored as strings).
However, in the snippet below they seem to be ordered randomly.
Neither are the order that the properties are added.
It should be noted:
Unlike what common belief suggests (perhaps due to other programming languages like delete in C++), the delete operator has nothing to do with directly freeing memory. Memory management is done indirectly via breaking references.
More info: delete operator and Memory Management.
var obj={}, // object to store properties (keys) and values
props=[]; // array to store property names
add.onclick=function(){
var prop=rnd(), val=rnd(); // get 2 random numbers
obj[ prop ] = val; // add property & value → object
props.push( prop ); // add property name → array
updateInfo(); // display object
}
del.onclick=function(){
var lastProp=props.pop(); // get/remove last property name in array
delete obj[ lastProp ]; // remove property
updateInfo(); //display object
}
function rnd(){return Math.floor(Math.random()*1E5);} // random 0-99999
function updateInfo(){ // show pretty object 😘
info.innerHTML=JSON.stringify(obj).replace(/[\{\}]+/g,"").replaceAll(',','<br>');
}
<button id='add'>add new property</button>
<button id='del'>delete last added</button>
<div id='info'></div>
I have a js 'associative' array, with
array['serial_number'] = 'value'
serial_number and value are strings.
e.g. array['20910930923'] = '20101102'
I sorted it by value, works fine.
Let's say I get back the object 'sorted';
Now I want to access the first KEY of the 'sorted' array.
How do I do it? I can't think I need an iteration with
for (var i in sorted)
and just stop after ther first one...
thanks
edit: just to clarify, I know that js does not support associative arrays (that's why I put it in high commas in the Title).
2021 Update
Since ES6, properties with string keys are enumerated in insertion order. Here's a nice summary. My original answer from 2010 was correct at the time and is preserved below:
Original answer
JavaScript object properties are specified to have no order, much though many people wish it were different. If you need ordering, abandon any attempt to use an object and use an Array instead, either to store name-value objects:
var nameValues = [
{name: '20910930923', value: '20101102'},
{name: 'foo', value: 'bar'}
];
... or as an ordered list of property names to use with your existing object:
var obj = {
'20910930923': '20101102',
'foo': 'bar'
};
var orderedPropertyNames = ['20910930923', 'foo'];
Try this:
// Some assoc list
var offers = {'x':{..some object...}, 'jjj':{...some other object ...}};
// First element (see attribution below)
return offers[Object.keys(offers)[0]];
// Last element (thanks to discussion on finding last element in associative array :)
return offers[Object.keys(offers)[Object.keys(offers).length - 1]];
Actually JavaScript doesn't support associative arrays, so you can't loop through it in an implied order (e.g. you can't access it via the indexer property array[0] won't access the first element in your object). The syntax is what makes it look like it does, but in reality it doesn't. So you have no "Order" to your objects.
http://www.hunlock.com/blogs/Mastering_Javascript_Arrays
Javascript does not have, and does not
support Associative Arrays. However…
All arrays in Javascript are objects
and Javascript's object syntax gives a
basic emulation of an associative
Array. For this reason the example
code above will actually work. Be
warned that this is not a real array
and it has real pitfals if you try to
use it. The 'person' element in the
example becomes part of the Array
object's properties and methods, just
like .length, .sort(), .splice(), and
all the other built-in properties and
methods.
Just thinking off the top of my head, but could you have another array with the key value pairs swapped?
So the answer would be arrayKeyValueReversed['20101102'] = '20910930923';
When you sort the array, use the first item (array[0]) as the key to get the value in the arrayKeyValueReversed.
I have a custom object which contains other items (ie arrays, strings, other types of objects).
I am not sure how to traverse the object to iterate and list all of the object types, keys, and values of the nested items.
Second to this issue I don't know how many levels of nesting there are (as the object is generated dynamically from the back-end and passed to me as one object).
Any ideas (and should I just use javascript/jQuery or both to do this most efficiently)?
Thanks I'll give the code a go. I am retrieving a result set from a webservice which returns a different set of columns (of differing datatypes) and rows each time. I don't know the names of the columns which is why I am trying to get the data however I can.
Depending on the datatype I will perform a different action (sum the amount, format it etc).
JSON-serialized objects contain a hierarchy, w/o any reference cycles, so it should be fairly straightforward to traverse, something like
function visit(JSONobj, f)
{
for (var key in JSONobj)
{
var value = JSONobj[key];
f(key,value);
if (value instanceof Object)
visit(value, f);
}
}
where f is a function that does something with keys and values. (of course you could just write a function to do this directly).
What exactly are you trying to find within the object?