How to turn nested plain js objects into Ember.js objects? - javascript

If I have a nested set of plain old javascript objects (for example, having been returned from JSON), how do I them into Ember.js objects (or at least getting the binding functionality working)?
For example, if I have an object like:
var x = {
bar: {
baz: "quux"
}
}
Then I turn that into an Ember object:
var y = Ember.Object.create(x);
Then setting the value of "baz" won't update any views I have, because it is just a normal js object, not an Ember object.
I know I can just recursively go over the object keys, and do Ember.Object.create all the way down, but is there an alternative approach?

I'm not sure how you're attempting to set the value of baz, after you've created the Ember.Object, but you should make sure you use an observer-aware setter function. For this example, I'd suggest using setPath().
For example:
var x = {
bar: {
baz: "quux"
}
};
var y = Ember.Object.create(x);
y.setPath('bar.baz', 'foo');
jsFiddle example, showing a view update after setting: http://jsfiddle.net/ebryn/kv3cU/

Here's my version:
import { typeOf } from '#ember/utils'
import EmberObject from '#ember/object'
export default function deepEmberObject(anything) {
if (typeOf(anything) === 'array') {
return anything.map(a => deepEmberObject(a))
} else if (typeOf(anything) === 'object') {
let converted = Object.keys(anything).reduce((acc, k) => {
acc[k] = deepEmberObject(anything[k])
return acc
}, {})
return EmberObject.create(converted)
} else {
return anything
}
}
test:
import deepEmberObject from 'zipbooks/utils/deep-ember-object'
import { module, test } from 'qunit'
module('Unit | Utility | deep-ember-object', function() {
test('it works', function(assert) {
let result = deepEmberObject({ pandas: [{ id: 3, children: [{ name: 'Bobby', features: { weight: 3 } }] }] })
assert.equal(
result
.get('pandas')[0]
.get('children')[0]
.get('features')
.get('weight'),
3
)
})
})

For some reason I had to define nested objects independently in order to ensure the computed works properly (even the enumerated ones).
For that I end up crafting these 2 utility functions:
import EmberObject from '#ember/object';
import { A } from '#ember/array';
function fromArrayToEmberArray(array) {
const emberArray = A();
array.forEach(function(item) {
if (Array.isArray(item)) {
emberArray.push(fromArrayToEmberArray(item));
} else if (item && typeof item === 'object') {
emberArray.push(fromObjectToEmberObject(item));
} else {
emberArray.push(item);
}
});
return emberArray;
}
function fromObjectToEmberObject(pojo) {
const emberObject = EmberObject.create();
for (const key in pojo) {
const keyObject = pojo[key];
if (Array.isArray(keyObject)) {
emberObject.set(key, fromArrayToEmberArray(keyObject))
} else if (keyObject && typeof keyObject === 'object') {
emberObject.set(key, fromObjectToEmberObject(keyObject))
} else {
emberObject.set(key, keyObject);
}
}
return emberObject;
}
export default {fromObjectToEmberObject};

Related

How can I count a specific property in an unknown object tree in JavaScript? [duplicate]

Is there a way (in jQuery or JavaScript) to loop through each object and it's children and grandchildren and so on?
If so... can I also read their name?
Example:
foo :{
bar:'',
child:{
grand:{
greatgrand: {
//and so on
}
}
}
}
so the loop should do something like this...
loop start
if(nameof == 'child'){
//do something
}
if(nameof == 'bar'){
//do something
}
if(nameof =='grand'){
//do something
}
loop end
You're looking for the for...in loop:
for (var key in foo)
{
if (key == "child")
// do something...
}
Be aware that for...in loops will iterate over any enumerable properties, including those that are added to the prototype of an object. To avoid acting on these properties, you can use the hasOwnProperty method to check to see if the property belongs only to that object:
for (var key in foo)
{
if (!foo.hasOwnProperty(key))
continue; // skip this property
if (key == "child")
// do something...
}
Performing the loop recursively can be as simple as writing a recursive function:
// This function handles arrays and objects
function eachRecursive(obj)
{
for (var k in obj)
{
if (typeof obj[k] == "object" && obj[k] !== null)
eachRecursive(obj[k]);
else
// do something...
}
}
You can have an Object loop recursive function with a property execute function propExec built within it.
function loopThroughObjRecurs (obj, propExec) {
for (var k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
loopThroughObjRecurs(obj[k], propExec)
} else if (obj.hasOwnProperty(k)) {
propExec(k, obj[k])
}
}
}
Test here:
// I use the foo object of the OP
var foo = {
bar:'a',
child:{
b: 'b',
grand:{
greatgrand: {
c:'c'
}
}
}
}
function loopThroughObjRecurs (obj, propExec) {
for (var k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
loopThroughObjRecurs(obj[k], propExec)
} else if (obj.hasOwnProperty(k)) {
propExec(k, obj[k])
}
}
}
// then apply to each property the task you want, in this case just console
loopThroughObjRecurs(foo, function(k, prop) {
console.log(k + ': ' + prop)
})
If you want to get back a tree of relationships you can use Object.keys recursively.
function paths(item) {
function iter(r, p) {
var keys = Object.keys(r);
if (keys.length) {
return keys.forEach(x => iter(r[x], p.concat(x)));
}
result.push(p);
}
var result = [];
iter(item, []);
return result;
}
var data = {
foo: {
bar: '',
child: {
grand: {
greatgrand: {}
}
}
}
};
console.log(paths(data));
This can be extended to search for values within an object structure that match a function:
function objectSearch(rootItem, matcher) {
const visited = [];
const paths = [];
function iterate(item, path) {
if (visited.includes(item)) {
return;
}
visited.push(item);
if (typeof item === "object" && item !== null) {
var keys = Object.keys(item);
if (keys.length) {
return keys.forEach(key => iterate(item[key], path.concat(key)));
}
}
if (matcher(item)) {
paths.push(path);
}
}
iterate(rootItem, []);
return paths;
}
function searchForNaNs(rootItem) {
return objectSearch(rootItem, (v) => Object.is(NaN, v));
}
var banana = {
foo: {
bar: "",
child: {
grand: {
greatgrand: {},
nanan: "NaN",
nan: NaN,
},
},
},
};
console.log("There's a NaN at", searchForNaNs(banana)[0].join("."), "in this object:", banana);
Consider using object-scan. It's powerful for data processing once you wrap your head around it.
One great thing is that the items are traversed in "delete safe" order. So if you delete one, it won't mess up the loop. And you have access to lots of other properties like parents etc.
// const objectScan = require('object-scan');
const obj = { foo: { bar: '', child: { grand: { greatgrand: { /* and so on */ } } } } };
objectScan(['**'], {
filterFn: ({ property }) => {
console.log(property);
}
})(obj);
// => greatgrand
// => grand
// => child
// => bar
// => foo
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
I would recommend using sindresorhus's map-obj & filter-obj utilities ...

Double for loop without mutating prop, VUE3

I have a 'data' props which say looks like this:
data = [
{
"label":"gender",
"options":[
{"text":"m","value":0},
{"text":"f","value":1},
{"text":"x", "value":null}
]
},
{
"label":"age",
"options":[
{"text":"<30", "value":0},
{"text":"<50","value":1},
{"text":">50","value":3}
]
}
]
In a computed property I want to have a new array which looks exactly like the data prop, with the difference that - for the sake of example let's say - I want to multiply the value in the options array by 2. In plain js I did this before, like this:
data.forEach(item => {
item.options.forEach(option => {
if (option.value !== null && option.value !== 0) {
option.value *= 2;
}
})
});
Now I'm trying to do this in a computed property, with .map(), so it doesn't mutate my data props, but I cant figure out how.
computed: {
doubledValues() {
var array = this.data.map((item) => {
//...
item.options.map((option) => {
//... option.value * 2;
});
});
return array;
}
}
you can use map() method, like so:
computed: {
doubledValues() {
return this.data.map(item => ({...item, options: item.options.map(obj => {
return (obj.value != null) ? { ...obj, value: obj.value * 2 } : { ...obj }
})})
);
}
}
Just copy objects/arrays. It will be something like that
computed: {
doubledValues() {
return this.data.map((item) => {
const resultItem = {...item};
resultItem.options = item.options.map((option) => {
const copyOption = {...option};
if (copyOption.value !== null && copyOption.value !== 0) {
copyOption.value *= 2;
}
return copyOption;
});
return resultItem;
});
}
}

Need a custom assignment implementaion

I am working with some state management application where I have a data structure as follows
const mainObject = {
firstLevel: {
secondLevel: {
thirdLevel: {
actualProperty: 'Secret'
}
}
},
firstLevelUntouched:{
secondLevelUntouched:{
thirdLevelUntouched:{
untouchedProperty:'I don`t want to change'
}
}
}
};
I want to change the actualProperty to a new value which out a deepClone
I did it with the following code
const modified = {
...mainObject,
...{
firstLevel: {
...mainObject.firstLevel,
...{
secondLevel: {
...mainObject.firstLevel.secondLevel,
thirdLevel: {
...mainObject.firstLevel.secondLevel.thirdLevel,
actualProperty: 'New secret'
}
}
}
}
}
}
But its looks like Bulky Code. So I need to write a function like
modified = myCustomAssignment(mainObject, ['firstLevel', 'secondLevel', 'thirdLevel', 'actualProperty'], 'New secret')
Can anyone help me on this?
You could use a simple traversal function for this that just traverses the passed properties until it arrives as the final one, then sets that to the new value.
function myCustomAssignment(mainObject, propertyList, newValue) {
const lastProp = propertyList.pop();
const propertyTree = propertyList.reduce((obj, prop) => obj[prop], mainObject);
propertyTree[lastProp] = newValue;
}
You could even add propertyList = propertyList.split('.') to the top of this function so the list can be passed in as an easy-to-read string, like myCustomAssignment(mainObject, 'firstLevel.secondLevel.thirdLevel.actualProperty', 'new value') if you wanted that.
export function mutateState(mainObject: object, propertyList: string[], newValue: any) {
const lastProp = propertyList.pop();
const newState: object = { ...mainObject };
const propertyTree =
propertyList
.reduce((obj, prop) => {
obj[prop] = { ...newState[prop], ...obj[prop] };
return obj[prop];
}, newState);
propertyTree[lastProp] = newValue;
return newState as unknown;
}
This fixed my issue. thanks all..

Dynamically get every key value of a JSON in Javascript [duplicate]

Is there a way (in jQuery or JavaScript) to loop through each object and it's children and grandchildren and so on?
If so... can I also read their name?
Example:
foo :{
bar:'',
child:{
grand:{
greatgrand: {
//and so on
}
}
}
}
so the loop should do something like this...
loop start
if(nameof == 'child'){
//do something
}
if(nameof == 'bar'){
//do something
}
if(nameof =='grand'){
//do something
}
loop end
You're looking for the for...in loop:
for (var key in foo)
{
if (key == "child")
// do something...
}
Be aware that for...in loops will iterate over any enumerable properties, including those that are added to the prototype of an object. To avoid acting on these properties, you can use the hasOwnProperty method to check to see if the property belongs only to that object:
for (var key in foo)
{
if (!foo.hasOwnProperty(key))
continue; // skip this property
if (key == "child")
// do something...
}
Performing the loop recursively can be as simple as writing a recursive function:
// This function handles arrays and objects
function eachRecursive(obj)
{
for (var k in obj)
{
if (typeof obj[k] == "object" && obj[k] !== null)
eachRecursive(obj[k]);
else
// do something...
}
}
You can have an Object loop recursive function with a property execute function propExec built within it.
function loopThroughObjRecurs (obj, propExec) {
for (var k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
loopThroughObjRecurs(obj[k], propExec)
} else if (obj.hasOwnProperty(k)) {
propExec(k, obj[k])
}
}
}
Test here:
// I use the foo object of the OP
var foo = {
bar:'a',
child:{
b: 'b',
grand:{
greatgrand: {
c:'c'
}
}
}
}
function loopThroughObjRecurs (obj, propExec) {
for (var k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
loopThroughObjRecurs(obj[k], propExec)
} else if (obj.hasOwnProperty(k)) {
propExec(k, obj[k])
}
}
}
// then apply to each property the task you want, in this case just console
loopThroughObjRecurs(foo, function(k, prop) {
console.log(k + ': ' + prop)
})
If you want to get back a tree of relationships you can use Object.keys recursively.
function paths(item) {
function iter(r, p) {
var keys = Object.keys(r);
if (keys.length) {
return keys.forEach(x => iter(r[x], p.concat(x)));
}
result.push(p);
}
var result = [];
iter(item, []);
return result;
}
var data = {
foo: {
bar: '',
child: {
grand: {
greatgrand: {}
}
}
}
};
console.log(paths(data));
This can be extended to search for values within an object structure that match a function:
function objectSearch(rootItem, matcher) {
const visited = [];
const paths = [];
function iterate(item, path) {
if (visited.includes(item)) {
return;
}
visited.push(item);
if (typeof item === "object" && item !== null) {
var keys = Object.keys(item);
if (keys.length) {
return keys.forEach(key => iterate(item[key], path.concat(key)));
}
}
if (matcher(item)) {
paths.push(path);
}
}
iterate(rootItem, []);
return paths;
}
function searchForNaNs(rootItem) {
return objectSearch(rootItem, (v) => Object.is(NaN, v));
}
var banana = {
foo: {
bar: "",
child: {
grand: {
greatgrand: {},
nanan: "NaN",
nan: NaN,
},
},
},
};
console.log("There's a NaN at", searchForNaNs(banana)[0].join("."), "in this object:", banana);
Consider using object-scan. It's powerful for data processing once you wrap your head around it.
One great thing is that the items are traversed in "delete safe" order. So if you delete one, it won't mess up the loop. And you have access to lots of other properties like parents etc.
// const objectScan = require('object-scan');
const obj = { foo: { bar: '', child: { grand: { greatgrand: { /* and so on */ } } } } };
objectScan(['**'], {
filterFn: ({ property }) => {
console.log(property);
}
})(obj);
// => greatgrand
// => grand
// => child
// => bar
// => foo
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
I would recommend using sindresorhus's map-obj & filter-obj utilities ...

loop properties of javascript object

i have a function that loop all object properties and return value if it qualify certain condition
basically this is how i m doing
//an enum
var BillingType = Object.freeze({
PayMonthly: { key: 'Monthly', value: 1 },
PayYearly: { key: 'Yearly', value: 2 }
});
now to make it work i do this
for (var property in BillingType ) {
if (BillingType .hasOwnProperty(property)) {
if (value === BillingType [property].value) {
return BillingType [property].key;
}
}
}
it works fine but to make it generic for all enums i changed code to
getValue = function (value, object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (value === object[property].value) {
return object[property].key;
}
}
}
}
now when i try to call from other functions
enumService.getValue(1, 'BillingModel');
rather to loop all properties it start loop on its characters.
how can i convert string to object or m doing it totally wrong . any help will be appreciated
Regards
Your getValue looks fine, just call it using
enumService.getValue(1, BillingModel); // <-- no quotes
and here is a working fiddle: http://jsfiddle.net/LVc6G/
and here is the code of the fiddle:
var BillingType = Object.freeze({
PayMonthly: { key: 'Monthly', value: 1 },
PayYearly: { key: 'Yearly', value: 2 }
});
var getValue = function (value, object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (value === object[property].value) {
return object[property].key;
}
}
}
};
alert(getValue(1, BillingType));

Categories