Stop overriding object once it has been set dynamically [closed] - javascript

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I'm creating an object dynamically using dot notation. This part works fine! The issue I have is that I'm overwriting object on each iteration. Once it's set it should just add to the object to overwrite and replace the properties.
What am I doing wrong ?
E.g. price": { "minPrice": 1000, "maxPrice": 10000 } not { "minPrice": value} or { "maxPrice": value }.
ausedSet(params, key, value);
function ausedSet(object, key, value) {
var keys = key.split('.');
for (var i = 0; i < keys.length - 1; i++) {
object = object[keys[i]] = {};
}
object[keys[keys.length - 1]] = value;
return object;
}
function getValidParams(value, key) {
if (acceptedParameters.indexOf(key) > -1) {
populateObject(vm.filterParameters, key, value);
}
}
//vm.filterParameters returns these
var acceptedParameters = [
'postCode',
'distance',
'locale',
'vehicleCategory',
'resultOrder',
'longLatCoordinates',
'price.minPrice',
'price.maxPrice',
'pagination'
]

The issue here is that you're not saving a reference to the original object... you're overwriting your object variable in the for-loop and then returning the final value of that variable. Perhaps you should add a line like:
var current = object;
and then use current instead of object in the rest of your function. Then you can return object and it will be still be a reference to your original input.
Edit: OP wasn't using the return value to begin with, so my original solution didn't help. The problem was that OP was passing in related sets of keys ("price.minPrice", "price.maxPrice") and the function was overwriting the value of the initial shared key ("price"). The solution is to check if an object already exists at the key in question before assigning an empty one.
function populateObject(obj, keyString, value) {
var keys = keyString.split('.'),
curr = obj;
for (var i = 0; i < keys.length - 1; i++) {
if (!curr[keys[i]]) curr[keys[i]] = {};
curr = curr[keys[i]];
}
curr[keys[keys.length - 1]] = value;
return obj;
}
var myObject = { foo: 'bar' };
var result = populateObject(myObject, 'some.nested.keys', 'something');
result = populateObject(result, 'some.nested.other', 'test');
console.log(result === myObject);
console.log(result.foo === 'bar');
console.log(result.some.nested.keys === 'something');
console.log(result.some.nested.other === 'test');

Related

How can i update values for all keys in nested object at once in react js? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I have object like below, in the below object i save the preferences as true/false
now i want to update status for all at once either true/false for cases like checked all or un-checked all.
obj = {
a:false,
b:{
c:{
e:{
f:false,
g:true,
},
h:{
i:false,
j:true,
}
}
},
k:{
l:false,
m:false
},
n:true
}
I am thinking of using recursive function and i can access values for all keys with recursive function
but i need best approach to update state.
For very deeps Arrays, the straight forward approach might get slow,
I thought of a way of doing it with string replace instead.
const obj = {
a:false,
b:{
c:{
e:{
f:false,
g:true,
},
h:{
i:false,
j:true,
}
}
},
k:{
l:false,
m:false
},
n:true
};
function checkAll(obj,state = true) {
const regex = state ? /true/gi : /false/gi;
const changeTo = state ? 'false' : 'true';
// the magic, using string replace to set all at once
return JSON.parse(JSON.stringify(obj).replace(regex, changeTo));
}
function uncheckAll(obj) {
return checkAll(obj,false);
}
console.log(checkAll(obj));
console.log(uncheckAll(obj));
Can't tell you how performant it is, but I think the complexity will stay the same no matter how deep the arrays are.
A recursive function will indeed do what you need, it could look like this:
const obj = {
a:false,
b:{
c:{
e:{
f:false,
g:true,
},
h:{
i:false,
j:true,
}
}
},
k:{
l:false,
m:false
},
n:true
};
const setAllTo = (val, object) => Object.keys(object)
.reduce((prev, curr) => ({
...prev,
[curr]: typeof object[curr] === 'object' ?
setAllTo(val, object[curr]) : val
}), {})
const result = setAllTo(true, obj);
console.dir(result)
Not exactly sure where you are stuck but this should do:
function setState(obj, value) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (typeof obj[prop] === 'object') {
setState(obj[prop], value);
} else {
obj[prop] = value;
}
}
}
}
setState(obj, true);
Second argument for setState function sets the value for each property.
You can do it using loops but I think recursive example is more elegant.
What you need is to approach this problem in small steps:
First you pass the whole object
Then iterate through each property of that object
If object's property is also an object then you pass that particular
property to setState function again and now that function works with
even smaller portion of object
if property is not an object, then inside else statement we assume that it contains boolean value so we simply assign true value to it. So the function works until it reaches all the properties that contain boolean values.

simple javascript substitute cipher [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm trying to cipher a result of a sum into a base64 code.
var map = {
1: 'dG',
2: 'h2, 3: '
gF,
4: 'pz',
5: 'V0'
};
if (map.indexOf(sum1) > -1) {
ans = map.indexOf(sum1)
} else {
console.log("Incorrect");
}
I want to match my number (sum1) to the index of my array and save the letters to the variable ans.
I am new to programming and would appreciate if anyone can help me here. If I didn't explain well enough please ask for clarification.
The indexOf method is for arrays/strings so that is not going to work since you have an Object. You idea was right to check to see if the property exists, you just are doing it the wrong way.
The best way of checking to see if the property exists in an object is to use hasOwnProperty.
var map = {
1: 'dG',
2: 'h2',
3: 'gF',
4: 'pz',
5: 'V0'
};
function getText (obj, key) {
// if(obj.hasOwnProperty(key)){
// return obj[key];
// } else {
// return null;
// }
return obj.hasOwnProperty(key) ? obj[key] : null;
}
console.log(getText(map, 1));
console.log(getText(map, 5));
console.log(getText(map, 100));
But since you have strings, you do not have to use hasOwnProperty, you can just use a truthy check since strings evaluate to true
var map = {
1: 'dG',
2: 'h2',
3: 'gF',
4: 'pz',
5: 'V0'
};
function getText (obj, key) {
// if (obj[key]) {
// return obj[key];
// } else {
// return null;
// }
return obj[key] ? obj[key] : null;
}
console.log(getText(map, 1));
console.log(getText(map, 5));
console.log(getText(map, 100));
Or just use an array and not an object to hold your strings. The thing with arrays is the first index is ZERO so all the indexes would shift down one. So to make the 1 in your object match 1 in the array you have to put something in index 0.
var map = [null, 'dG', 'h2', 'gF', 'pz', 'V0'];
function getText (obj, key) {
// if (obj[key]) {
// return obj[key];
// } else {
// return null;
// }
return obj[key] ? obj[key] : null;
}
console.log(getText(map, 1));
console.log(getText(map, 5));
console.log(getText(map, 100));
I decided to augment given your comment in your question. Note that NONE of this has anything to do with a cipher...I will leave that to you to refactor in.
Fixed syntax in your object (it IS an object)
added ways to get both the keys and values of your object given its simple layout
use the values of your object obtained from the object as an array (values)
created a function passing in those values and one I wanted to look for
returned an object with some fun properties to show how to do that
var myObject = {
1: 'dG',
2: 'h2',
3: 'gF',
4: 'pz',
5: 'V0'
};
function doThing(myvalues, sum1) {
var ans = {
checked: sum1,
exists: false,
index: -1
};
var stuffindex = myvalues.indexOf(sum1);
var stuffExists = (stuffindex > -1);
ans.exists = stuffExists;
ans.index = stuffindex;
// console.log(ans);
return ans;
}
var keys = Object.keys(myObject);
console.log(keys);
var values = Object.values(myObject);
console.log(values);
console.log(doThing(values, 'pz'));
var checkfor = 'gF';
console.log("Compare:", checkfor == "gf");
console.log(checkfor, " exists? ", doThing(values, checkfor).exists);
console.log('gf', " exists? ", doThing(values, 'gf').exists);

Access sub-property with generic/dynamic property list [duplicate]

I have a bunch of object attributes coming in as dot-delimited strings like "availability_meta.supplier.price", and I need to assign a corresponding value to record['availability_meta']['supplier']['price'] and so on.
Not everything is 3 levels deep: many are only 1 level deep and many are deeper than 3 levels.
Is there a good way to assign this programmatically in Javascript? For example, I need:
["foo.bar.baz", 1] // --> record.foo.bar.baz = 1
["qux.qaz", "abc"] // --> record.qux.qaz = "abc"
["foshizzle", 200] // --> record.foshizzle = 200
I imagine I could hack something together, but I don't have any good algorithm in mind so would appreciate suggestions. I'm using lodash if that's helpful, and open to other libraries that may make quick work of this.
EDIT this is on the backend and run infrequently, so not necessary to optimize for size, speed, etc. In fact code readability would be a plus here for future devs.
EDIT 2 This is NOT the same as the referenced duplicate. Namely, I need to be able to do this assignment multiple times for the same object, and the "duplicate" answer will simply overwrite sub-keys each time. Please reopen!
You mentioned lodash in your question, so I thought I should add their easy object set() and get() functions. Just do something like:
_.set(record, 'availability_meta.supplier.price', 99);
You can read more about it here: https://lodash.com/docs#set
These functions let you do more complex things too, like specify array indexes, etc :)
Something to get you started:
function assignProperty(obj, path, value) {
var props = path.split(".")
, i = 0
, prop;
for(; i < props.length - 1; i++) {
prop = props[i];
obj = obj[prop];
}
obj[props[i]] = value;
}
Assuming:
var arr = ["foo.bar.baz", 1];
You'd call it using:
assignProperty(record, arr[0], arr[1]);
Example: http://jsfiddle.net/x49g5w8L/
What about this?
function convertDotPathToNestedObject(path, value) {
const [last, ...paths] = path.split('.').reverse();
return paths.reduce((acc, el) => ({ [el]: acc }), { [last]: value });
}
convertDotPathToNestedObject('foo.bar.x', 'FooBar')
// { foo: { bar: { x: 'FooBar' } } }
Just do
record['foo.bar.baz'] = 99;
But how would this work? It's strictly for the adventurous with a V8 environment (Chrome or Node harmony), using Object.observe. We observe the the object and capture the addition of new properties. When the "property" foo.bar.baz is added (via an assignment), we detect that this is a dotted property, and transform it into an assignment to record['foo']['bar.baz'] (creating record['foo'] if it does not exist), which in turn is transformed into an assignment to record['foo']['bar']['baz']. It goes like this:
function enable_dot_assignments(changes) {
// Iterate over changes
changes.forEach(function(change) {
// Deconstruct change record.
var object = change.object;
var type = change.type;
var name = change.name;
// Handle only 'add' type changes
if (type !== 'add') return;
// Break the property into segments, and get first one.
var segments = name.split('.');
var first_segment = segments.shift();
// Skip non-dotted property.
if (!segments.length) return;
// If the property doesn't exist, create it as object.
if (!(first_segment in object)) object[first_segment] = {};
var subobject = object[first_segment];
// Ensure subobject also enables dot assignments.
Object.observe(subobject, enable_dot_assignments);
// Set value on subobject using remainder of dot path.
subobject[segments.join('.')] = object[name];
// Make subobject assignments synchronous.
Object.deliverChangeRecords(enable_dot_assignments);
// We don't need the 'a.b' property on the object.
delete object[name];
});
}
Now you can just do
Object.observe(record, enable_dot_assignments);
record['foo.bar.baz'] = 99;
Beware, however, that such assignments will be asynchronous, which may or may not work for you. To solve this, call Object.deliverChangeRecords immediately after the assignment. Or, although not as syntactically pleasing, you could write a helper function, also setting up the observer:
function dot_assignment(object, path, value) {
Object.observe(object, enable_dot_assignments);
object[path] = value;
Object.deliverChangeRecords(enable_dot_assignments);
}
dot_assignment(record, 'foo.bar.baz', 99);
Something like this example perhaps. It will extend a supplied object or create one if it no object is supplied. It is destructive in nature, if you supply keys that already exist in the object, but you can change that if that is not what you want. Uses ECMA5.
/*global console */
/*members split, pop, reduce, trim, forEach, log, stringify */
(function () {
'use strict';
function isObject(arg) {
return arg && typeof arg === 'object';
}
function convertExtend(arr, obj) {
if (!isObject(obj)) {
obj = {};
}
var str = arr[0],
last = obj,
props,
valProp;
if (typeof str === 'string') {
props = str.split('.');
valProp = props.pop();
props.reduce(function (nest, prop) {
prop = prop.trim();
last = nest[prop];
if (!isObject(last)) {
nest[prop] = last = {};
}
return last;
}, obj);
last[valProp] = arr[1];
}
return obj;
}
var x = ['fum'],
y = [
['foo.bar.baz', 1],
['foo.bar.fum', new Date()],
['qux.qaz', 'abc'],
['foshizzle', 200]
],
z = ['qux.qux', null],
record = convertExtend(x);
y.forEach(function (yi) {
convertExtend(yi, record);
});
convertExtend(z, record);
document.body.textContent = JSON.stringify(record, function (key, value, Undefined) {
/*jslint unparam:true */
/*jshint unused:false */
if (value === Undefined) {
value = String(value);
}
return value;
});
}());
it's an old question, but if anyone still looking for a solution can try this
function restructureObject(object){
let result = {};
for(let key in object){
const splittedKeys = key.split('.');
if(splittedKeys.length === 1){
result[key] = object[key];
}
else if(splittedKeys.length > 2){
result = {...result, ...{[splittedKeys.splice(0,1)]: {}} ,...restructureObject({[splittedKeys.join('.')]: object[key]})}
}else{
result[splittedKeys[0]] = {[splittedKeys[1]]: object[key]}
}
}
return result
}

Access multi-level property and its full path of properties

I'm currently struggling with a JavaScript problem. I want to return a multi-level property, as well as every variable contained within, by passing in the original object, and an array of paths to the properties I want.
For example, if I have the following object:
obj = {
product: {
candidate: {
id: 10,
reference: "test",
count: 4,
steps: 10
}
}
}
I want to be able to call a method:
getVarPath(obj, ["product.candidate.ID", "product.candidate.reference"])
And then have it return one object with each variable passed in the array, in it's original structure. So this would return an object looking like so:
{
product: {
candidate: {
id: 10,
reference: "test"
}
}
}
I do have this working in my local solution at the moment (passing in one string rather than an array at the moment).
The solution at the moment is pretty horrid but I'm looking to improve it, so if anyone can think of a better method that would be great.
Again, this is pretty horrid right now but I'm looking to improve it. But it does the job:
var getVarPath = function(obj, keys){
var elements = keys.split("."),
evalStr = "",
objStr = "obj",
newObjStr = "newObj",
newObj = {};
if(elements.length > 1){
elements.forEach(function(key, index){
// first append a property accessor at the end of the eval string
evalStr = evalStr + "['" + key + "']";
// if we're at the last element, we've reached the value, so assign it
if(index === elements.length -1){
eval(newObjStr + evalStr + " = " + objStr + evalStr);
}
else {
// if we're not at the last, we're at an object level
// if the nested object doesn't exist yet, create it
if(!eval(newObjStr + evalStr)){
eval(newObjStr + evalStr + " = {};");
}
}
});
}
return newObj;
}
For each element in the input array:
First, you can split the initial string: var nestedElements="product.candidate.ID".split(.)"
This returns an array with each level: ["product","candidate","ID"]
Now you can access to your nested object using each element of the array: obj["product"]["candidate"]["ID"] either by using a loop over the array or recursion.
var currentobj=obj;
for (var i=0;i<nestedElements.length;i++){
currentobj=currentobj[nestedElements[i]]
}
// currentobj is your id
In the same process, you could dynamically add elements to a new obj using a similar process:
newobj={} //before loop
if (newobj["product"] === undefined) newobj["product"]={} //in loop
And that should be done for each element on the input array, in the end is iterating through arrays and accessing the object using strings
Your code as-is shouldn't actually work. You're treating keys as a string, but passing in an array. You can (and should) avoid using eval(), by keeping track of the "inner" objects you're currently looking at, and using object[property] notation instead of object.property.
function getVarPath(obj, keys) {
var result = {};
// ["product.candidate.id", "product.candidate.reference"]
keys.forEach(function(key) {
var src = obj, // inner source object
dest = result, // inner destination object
parts = key.split(/\./);
// e.g. ["product", "candidate", "id"]
parts.forEach(function(part) {
// if we're looking at an object, make sure it exists in the dest
if (typeof(src[part]) === "object")
dest[part] = dest[part] || {};
// if it's just a value, copy it
else
dest[part] = src[part];
dest = dest[part]; // move from obj to obj.product, then to obj.product.candidate, etc.
src = src[part];
});
});
return result;
}
var obj = {
product: {
candidate: {
id: 10,
reference: "test",
count: 4,
steps: 10
}
}
}
var output = getVarPath(obj, ["product.candidate.id", "product.candidate.reference"]);
console.log(JSON.stringify(output));
Using _.propertyOf(), Array#reduce(), and Object.assign(), as well as computed property names, you could create a less daunting implementation:
function getVarPath(object, paths) {
return paths.reduce(function (accumulator, path) {
const that = _.propertyOf(accumulator)
let walk = path.split('.')
let value = this(walk)
for (let key = walk.pop(); key !== undefined; key = walk.pop()) {
const base = that(walk)
value = { [key]: value }
if (base !== undefined) {
value = Object.assign(base, value)
}
}
return Object.assign(accumulator, value)
}.bind(_.propertyOf(object)), {})
}
let obj = {
product: {
candidate: {
id: 10,
reference: "test",
count: 4,
steps: 10
}
}
}
console.log(getVarPath(obj, ['product.candidate.id', 'product.candidate.reference']))
<script src="https://cdn.rawgit.com/lodash/lodash/4.17.4/dist/lodash.min.js"></script>

How do I write a recursive function in Javascript to add up all of the string values of a deeply nested object?

Say I have this object:
{
"prop1":"Hello",
"prop2":{
"prop1":{
"prop1":"Tablecloth",
"prop2":"Indians"
},
"prop2":"JuicyJuice"
},
"prop3":"Sponge",
"prop4":{"Bob":"Squarepants"}
}
I would like a recursive function that will return HelloTableclothIndiansJuicyJuiceSpongeSquarepants.
Whatever object I put it, I want it to cycle though until it gets all of the strings and adds them all up.
Thank you!
Here's a very simple implementation that should work for simple objects like this:
var walkProps = function(obj) {
var s = "";
for(var x in obj)
{
if(typeof obj[x] === "string")
s += obj[x];
else
s += walkProps(obj[x]);
}
return s;
}
Demonstration
Note, though, that that depends on the order in which for-in visits the properties on the object, which is not specified and can vary by engine and by how the object is constructed (for instance, the order in which the properties were added).
Update: With some slight modification, this can be used to return the values based on the alphabetical order of the keys. This method is not sensitive to implementation-dependent ordering of properties:
var walkProps = function(obj) {
var s = "", i = 0, keys = Object.keys(obj).sort(), i;
for(; i < keys.length; i++)
{
if(typeof obj[keys[i]] === "string")
s += obj[keys[i]];
else
s += walkProps(obj[keys[i]]);
}
return s;
}
So even if "prop3" comes before "prop2" it will still return the same output.
Demonstration
You would need to write a function that loops over an object's properties and see if they are a string, and then append the strings to an output. If the property is an object rather than a string, you would want to call the function on this object and append it's return value to your total output.
You can loop over an object's properties using for...in like:
var MyObject = {
'a': 'string1',
'b': 'string2'
};
for (var key in MyObject) {
var value = MyObject[key];
}
To check if a property is a string you would want to do:
typeof value === "string"
Which will return true/false accordingly.
As mentioned, for( var b in a ) may not preserve ordering:
// Return array of string values
function getStrings(a) {
if( typeof(a) == "string" ) return [a];
var list = [];
for( var b in a ) list = list.concat(getStrings(a[b]));
return list;
}
Applied to OP's data:
var a = {
"prop1":"Hello",
"prop2":{
"prop1":{
"prop1":"Tablecloth",
"prop2":"Indians"
},
"prop2":"JuicyJuice"
},
"prop3":"Sponge",
"prop4":{"Bob":"Squarepants"}
}
getStrings(a).join(); // "Hello,Tablecloth,Indians,JuicyJuice,Sponge,Squarepants"
// Or as asked for by OP (again, order is not guaranteed)
getStrings(a).join(''); // "HelloTableclothIndiansJuicyJuiceSpongeSquarepants"

Categories

Resources