deconstruct JSON object in JS - javascript

I'm trying to deconstruct this object to get "Swiss franc", assuming i don't know the key CHF.
currencies = {CHF: {name: "Swiss franc", symbol: "Fr."}}
My attempts:
Object.values(Object.values(currencies)[0]
// -> Swiss franc,Fr.
Object.values(Object.values(currencies)[0].name
// -> S,w,i,s,s, ,f,r,a,n,c
// why does it split the string?? not retrieve the value based on key name??
// i know how to join the strings back, but i'm confused why is this result?)

just a little modification to your code:
const currencies = {CHF: {name: "Swiss franc", symbol: "Fr."}}
console.log(Object.values(Object.values(currencies)[0])[0])
// OR Simply
console.log(Object.values(currencies)[0].name)

Object.entries Implementation Reference
Explanation
The Object.entries() method returns an array of a given object's own
enumerable string-keyed property [key, value] pairs. This is the same
as iterating with a for...in loop, except that a for...in loop
enumerates properties in the prototype chain as well.
Working Example
const currencies = { CHF: { name: "Swiss franc", symbol: "Fr.", }}
console.log(Object.entries(currencies)[0][1].name);
Code Explanation
Object.entries(currencies)
The above statement return an array as below
[["CHF", {symbol: 'Fr.', name: 'Swiss franc'}]]
An array of combination of key value pairs in the object.
We are interested in the node zero of the array and index one of the first node of array.
So it will be
Object.entries(currencies)[0][1].name
Object.values implementation. Reference.
Explanation
The Object.values() method returns an array of a given object's own
enumerable property values, in the same order as that provided by a
for...in loop. (The only difference is that a for...in loop enumerates
properties in the prototype chain as well.)
Working Example
const currencies = { CHF: { name: "Swiss franc", symbol: "Fr.", }}
console.log(Object.values(currencies)[0].name);
Code Explanation
Object.values(currencies)
Tie above expression will return the array of values. Since we have only one key, the output of above statement will be
[{ symbol: 'Fr.', name: 'Swiss franc' }]
So the requird node will be
Object.values(currencies)[0].name

currencies = {CHF: {name: "Swiss franc", symbol: "Fr."}}
This will do it,
Object.values(currencies)[0].name
Breakdown :-
Object.values(currencies) gives [{name: "Swiss franc", symbol: "Fr."}]
Object.values(currencies)[0] gives {name: "Swiss franc", symbol: "Fr."}
Object.values(currencies)[0].name gives Swiss franc.

Related

How do I access value of an attribute with an object with no key pairs?

I am working with dynamoose and a query returns me the following output
[ Document { cost: 100 },
lastKey: undefined,
count: 1,
queriedCount: undefined,
timesQueried: 1 ]
When I try to do typeof(output) , it returns Object
When I try to do Object.keys(output) , it returns [ '0', 'lastKey', 'count', 'queriedCount', 'timesQueried' ]
When I do Object.entries(output), it returns [ [ '0', Document { cost: 100 } ],[ 'lastKey', undefined ], [ 'count', 1 ], [ 'queriedCount', undefined ], [ 'timesQueried', 1 ] ]
For my use, I need to get an Object which looks like this {cost: 100} and currently I am using
JSON.parse(JSON.stringify(output))
which gives me
[ { cost: 100 } ]
I havent encountered an object without a key value pair (I'm self taught), so I do not know how to approach this problem. And the current method seems inefficient and kinda wrong. Please advice me how I can do it in a proper way
JSON.stringify(output) //=> "[ { cost: 100 } ]"
What that means is that you have an array (denoted by []) with one item, which is an object (denoted by {}) with a single key cost.
Use [0] to get the first item in an array, and then use .cost to get the cost property.
console.log(output[0].cost) //=> 100
The reason that you are seeing other keys on that array is that it looks like that dynamoose library is adding some additional properties to the array before returning it. It's doing the equivalent of:
// vanilla js, not typescript
const output = [ { cost: 100 } ]
output.count = 1
output.timesQueried = 1
This gives the array properties in addition to the content. The dynamoose library appears to use this method to encode metadata about your queries alongside the returned data.
The default JSON serialization strategy for arrays does not serialize custom array properties, only the array members.

Javascript .map() on copied array

I have noticed that invoking .map() without assigning it to a variable makes it return the whole array instead of only the changed properties:
const employees = [{
name: "John Doe",
age: 41,
occupation: "NYPD",
killCount: 32,
},
{
name: "Sarah Smith",
age: 26,
occupation: "LAPD",
killCount: 12,
},
{
name: "Robert Downey Jr.",
age: 48,
occupation: "Iron Man",
killCount: 653,
},
]
const workers = employees.concat();
workers.map(employee =>
employee.occupation == "Iron Man" ? employee.occupation = "Philantropist" : employee.occupation
);
console.log(employees);
But considering that .concat() created a copy of the original array and assigned it into workers, why does employees get mutated as well?
This is happening because your objects within the array are still being referenced by same pointers. (your array is still referring to the same objects in memory). Also, Array.prototype.map() always returns an array and it's result should be assigned to a variable as it doesn't do in-place mapping. As you are changing the object's properties within your map method, you should consider using .forEach() instead, to modify the properties of the object within the copied employees array. To make a copy of your employees array you can use the following:
const workers = JSON.parse(JSON.stringify(employees));
See example below:
const employees = [
{
name: "John Doe",
age: 41,
occupation: "NYPD",
killCount: 32,
},
{
name: "Sarah Smith",
age: 26,
occupation: "LAPD",
killCount: 12,
},
{
name: "Robert Downey Jr.",
age: 48,
occupation: "Iron Man",
killCount: 653,
},
]
const workers = JSON.parse(JSON.stringify(employees));
workers.forEach(emp => {
if(emp.occupation == "Iron Man") emp.occupation = "Philantropist";
});
console.log("--Employees--")
console.log(employees);
console.log("\n--Workers--");
console.log(workers);
Note: If your data has any methods within it you need to use another method to deep copy
Problem analysis
workers = workers.map(employee =>
employee.occupation == "Iron Man" ? (employee.occupation = "Philantropist", employee) : (employee.occupation, employee)
);
[...] why does employees get mutated as well?
array.map() calls the passed function with each element from the array and returns a new array containing values returned by that function.
Your function just returns the result of the expression
element.baz = condition ? foo : bar;
which, depending on the condition, will
evaluate to foo or bar
assign that result to baz and
return that result
Further (expression1, expression2) will evaluate both expressions and return expression2 (see the comma operator).
So, although you return employee in both cases, you modify the original object with the left expression (expression 1).
Possible solution
You might want to create a new object using Object.assign()
array.map((employee) => Object.assign({ }, employee))
instead of using that array.concat() "trick". Using that mapping, you not only create a new array but also new objects with their attributes copied. Though this would not "deep copy" nested objects like { foo: { ... } } - the object accessible via the property foo would still be the same reference as the original. In such cases you might want to take a look on deep copying modules mentioned in the other answers.
The array references change but the copied array still reference the original objects in the original array. So any change in the objects in the array are reflected across all copies of the array. Unless you do a deep copy of the array, there is a chance that the some changes in the inner objects get reflected across each copy
What is the difference between a deep copy and a shallow copy?
Deep copies can be made in several ways. This post discusses specifically that: What is the most efficient way to deep clone an object in JavaScript?
map builds up a new array from the values returned from the callback, which caj easily be used to clone the objects in the array:
const workers = employees.map(employee => ({
...employee, // take everything from employee
occupation: employee.ocupation == "Iron Man" ? "Philantropist" : employee.occupation
}));
Or you could deep clone the array, then mutate it with a simple for:
const workers = JSON.parse(JSON.stringify(workers));
for(const worker of workers)
if(worker.ocupation == "Iron Man")
worker.ocupation = "Philantropist";

Lodash sorting & ordering deep object by values, without losing the key

I'm having a similar need to a previous post on this topic: needing to order and sort an object without losing the keys. However, I have an object of objects:
var o = {
123: { field: "science", name: "zed" },
234: { field: "tech", name: "sara" },
672: { field: "arts", name: "jon" }
}
_.fromPairs(_.sortBy(_.toPairs(o),function(a){ return a[1] }).reverse())
The above uses the lodash solution mentioned the other topic - I'm however not getting any consistency of results (tho am retaining the key!)
Any help would be appreciated.
I’m not clear on what value you intended to sort against above, since objects are incomparable. Let’s say you wanted to sort them by the name field, though — you would find that it still didn’t work.
First some background:
Historically, object properties were considered unordered in ES. The sequence that keys would be enumerated (e.g. by for ... in or Object.keys()) was implementation-specific.
However enumeration order became a specified behavior in ES2015.
That enumeration order codifies what most engines were already doing:
Keys which are integers are enumerated first, from lowest to highest.
String keys are enumerated in the order of assignment.
Symbol keys are enumerated in the order of assignment.
If you need to use integers as ordered keys, you’ll require a different data structure. Map is appropriate:
const o = {
123: { field: "science", name: "zed" },
234: { field: "tech", name: "sara" },
672: { field: "arts", name: "jon" }
}
const { compare } = new Intl.Collator();
const res = new Map(Object
.entries(o)
.sort(([ , a ], [ , b ]) => compare(a.name, b.name))
);
console.log([ ...res ]);
If you need to worry about old IE, an array of key-value entries would also suffice.

Javascript - Convert JSON Object Property Value from Array to String

I have an issue where an API call I'm using is sending objects with one property that contains a single array value (keys property in response below). Unfortunately I cannot use this format as I must abide by Nested arrays in order to use the outputted values in a separate application like so [[value1, value2, value3, value4],[value1, value2, value3, value4]]. I plan on asking a separate question to tackle the nested array section unless someone thinks it is an easy fix (I believe I should use .map to convert the object).
Here is the format of my objects (from console.log(searchQueries)):
[ { keys: [ 'hammer' ],
clicks: 1369,
impressions: 3151,
ctr: 0.4344652491272612,
position: 1.004443033957474 },
{ keys: [ 'woodmaking' ],
clicks: 207,
impressions: 6324,
ctr: 0.03273244781783681,
position: 4.35831752055661 },
{ keys: [ 'house trends' ],
clicks: 1,
impressions: 3,
ctr: 0.3333333333333333,
position: 4.666666666666666 },
{ keys: [ 'housing' ],
clicks: 1,
impressions: 36,
ctr: 0.027777777777777776,
position: 6.472222222222222 } ]
byProperty
Above response is passed from the following for-in loop the result of this API response array being nested in an object originally:
for (var prop in res){
searchQueries = res[prop];
console.log(searchQueries);
}
Would the JSON.stringify method or .toString('keys') achieve what I'm looking for?
If you want to turn keys from an array into a string, you just need to iterate over your array and make the change:
searchQueries.forEach(function (obj) { obj.keys = obj.keys[0] })
answer=Object.values(searchQueries).map(el=>{el.keys=el.keys[0];return Object.values(el)});
console.log(searchQueries);
https://jsbin.com/biyazunafu/1/edit?console
Loop over the main array, turn the Objects (el) keys array into a string, than turn the whole object into its values Array. However, Object.values is experimental, so may dont use this on the users side, instead use this code transpiled to ES5 :
answer=[];
for(key in searchQueries){
answer.push(searchQueries[key]);
}
answer=answer.map(function(el){
el.keys=el.keys[0];
var newel=[];
for(key in el){
newel.push(el[key]);
}
return newel;
});
1st Get the keys values
var keys = searchQueries.map(function(e){return e.keys;});
This will output :
[["hammer"], ["woodmaking"], ["house trends"], ["housing"]]
2nd: Concat the resulting array
var values = keys.join(',').split(',');
The result :
["hammer", "woodmaking", "house trends", "housing"]

How to convert an array of objects to an object in Lodash?

I have this:
[ { list:
[ [Object],
[Object] ] },
{ head:
[ [Object],
[Object] ] }
]
And want to turn it into this:
{ list:
[ [Object],
[Object] ],
head: [ [Object],
[Object] ]
}
So an array of objects into an object. It would be great to achieve this with lodash.
I think an even shorter solution would be:
Object.assign({}, ...array)
I know you asked for lodash, but it doesn't seem like you even need this way. Unless you want to use _.extend.
_.reduce(array, function(memo, current) { return _.assign(memo, current) }, {})
Here's a shorter version:
_.transform(array, _.ary(_.extend, 2), {});
The transform() function is like reduce(), except it's not expecting you to return anything. Since extend() is altering it's first argument, we can just pass it straight to transform(). It's wrapped in ary() to make sure it only gets 2 arguments passed to it.
To build on #rcsole's great answer, this works well:
states = [{
state: "NY",
name: "New York",
}, {
state: "AZ",
name: "Arizona",
}]
statesObj = Object.assign({}, ...states.map(state => {
return { [state.state]: state.name }
}))
Result:
{
AZ: "Arizona",
NY: "New York",
}
What's going on here?
Let's break this up into multiple pieces:
// Step 1: Transform from [{state: "Foo", name: "Bar"}, ...] to [{Foo: "Bar"}, ...]
mappedStates = states.map(state => { return { [state.state]: state.name } })
// Step 2: Merge all the objects in that array into a single new object
statesObj = Object.assign({}, ...mappedStates)
Step 1 uses map to iterate over every item in the array (every state object). map executes a function for every state object and returns a new object with the state as the key and the name as the value. We need to surround state.state in brackets because it's a dynamic value in an object literal.
Step 2 uses Object.assign to merge all the new state objects in the mappedStates array into a new object (the first parameter, {}).
What are the three dots ... for? That's the spread operator. It takes every element in the mappedStates array and turns them into direct arguments of the Object.assign method.
This example makes it clear:
Object.assign({}, ...mappedStates)
is the same as
Object.assign({}, {AZ: "Arizona"}, {NY: "New York"})
That's it!
Use fromPairs on lodash 4. https://lodash.com/docs#fromPairs
_.fromPairs([['fred', 30], ['barney', 40]]);

Categories

Resources