Merging some values in Javascript Object - javascript

I'm wondering if it is possible to merge certain values in an object to an array/object. I'm currently developing a module to create a quotation and everytime a value is changed in the form I want to send the form data to the server (async ajax call) to auto save the quotation. I'm getting the form data (by react hook form's getValues() function) as shown on the screenshot below:
As seen on the screenshot there are multiple lines[0] and lines[1 ] entries that are all seperate entries. To send it to the server I need the data as following:
client_id: "686",
condition: "7",
...
lines: {
0: {
amount: 1,
description: 'Test line 1',
price: 100,
},
1: {
amount: 1,
description: 'Test line 2',
price: 200,
}
},
...
Anyone here who knows how I can accomplish to convert my data into something like the example above? Thanks in advance!

Here is a solution for any key with dots.
Expanded the solution for the square brackets:
var source = {
id:12,
cond:"true",
'lines[0].amount':"1",
'lines[0].description':"a1",
'lines[1].amount':"1",
'lines[1].description':"a1",
};
var target = {};
for (var key in source) {
if (source.hasOwnProperty(key)) {
set(target, key, source[key])
}
}
function set(obj, path, value) {
obj = typeof obj === 'object' ? obj : {};
path = path.replace("[",".").replace("]","")
var keys = Array.isArray(path) ? path : path.split('.');
var curStep = obj;
for (var i = 0; i < keys.length - 1; i++) {
var key = keys[i];
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key)){
var nextKey = keys[i+1];
var useArray = /^\+?(0|[1-9]\d*)$/.test(nextKey);
curStep[key] = useArray ? [] : {};
}
curStep = curStep[key];
}
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
};

In general for merging values, folks should check out Object.assign().
In this particular case, to go from the second screenshot to the first, you might have used a loop, something like:
let newObject = {};
for (let lineIndex=0; lineIndex<lines.length; lineIndex++) {
for (let fieldIndex=0; fieldIndex<Object.keys(lines[lineIndex]).length; fieldIndex++) {
let newKey = "lines["+lineIndex+"]."+fieldIndex<Object.keys(lines[lineIndex])[fieldIndex];
newObject[newKey] = lines[lineIndex][Object.keys(lines[lineIndex])[fieldIndex]]
}
}
To go the other way, you would basically do the inverse. The details of the best way to do this would depend on how predictable your set of fields are and if you know the length. Otherwise you could make guesses by iterating over the keys in the flatter object and doing tests like if(Object.keys(flatterObject)[index].startsWith("lines[")).

Yes, there is a way to do what you want, but before any of that I'd suggest a deeper dive on the library you're using. Here's how to handle arrays in React Hook Form: https://react-hook-form.com/advanced-usage#FieldArrays
After "fixing" that, if you still want lines: {} to be an object, not an array, then a simple array reduce can build that up for you.

Related

Get a key in a JavaScript object by its value and rename it

I have a list of variables that I will form into an array with keys and values. However, before making them as array I want to check if any of their values matches a
specific string and change their keys names into something else.
Lets say this is the list of variables
customer1 = "Jack", customerDate1 = "08/13/2021", customer2 = "Michael", customerDate2 = "01/01/2021", customer3 = "Luna",customerDate3 = "03/10/2021";
The array before running condition check will be
data = [{key1:"Jack", keyDate1:"08/13/2021",key2:"Michael", keyDate2:"01/01/2021",key3:"Luna", keyDate3:"10" }];
Lets say the condition is:
customerName = "Jack";
I want the cross check customerName variable with the following variables (customer1,customer2,customer3) and if the condition meets any of them, then their keys in the array changes:
for example the condition meets customer1 then both keys of customer1 and customerDate1 changes to something else, to become something like
data = [{conditionMet1:"Jack", conditionDateMet1:"08/13/2021",key2:"Michael", keyDate2:"01/01/2021",key3:"Luna", keyDate3:"10" }];
I am appreciating any help or guidance.
A little bit tricky as alghoritm but it works:
let data = [{key1:"Jhon", keyDate1:"08/13/2021",key2:"Eric", keyDate2:"01/01/2021",key3:"Jack", keyDate3:"10" }];
let i = 1;
let result = [];
let explored = [];
data.forEach(x => {
let resultObj = {};
for (const [key, val] of Object.entries(x)) {
let newKey = key;
let newKeyDate = null;
if (val === "Jack") {
newKey = "conditionMet" + i;
newKeyDate = "conditionDateMet" + i;
}
if (!explored.includes(key)) resultObj[newKey] = val;
if (newKeyDate) {
resultObj[newKeyDate] = x["keyDate" + i];
explored.push("keyDate" + i)
}
if(!key.includes("Date")) i++;
}
result.push(resultObj)
})
console.log(result)
Basically for each element in data I explore all the entries and if I found condition I add conditionMet1 and conditionDateMet1 to object, otherwise what I found on object itself.
Perhaps not very elegant, but for the example given you could do something like this:
if(specialCondition){
data = [{specialKey1:customer1, specialDate1:customerDate1,key2:customer2...];
}else{
data = [{key1:customer1, keyDate1:customerDate1,key2:customer2...];
}
I don't think it is possible to rename a key but you can always create a new key by
object["newKeyName"] = object.previousKey
And optionally you can remove the old key by
delete object.previousKey
That way you will remove the previous key if you don't want it anymore.
Say you have 6 variables.
customer1, customerDate1, customer2, customerDate2, customer3,customerDate3;
You could make an array, by customer
const data = [{
id: customer1,
date: customerDate1
},{
id: customer2,
date: customerDate2
},{
id: customer3,
date: customerDate3
}];
This is now quite useful. Say you want to know which customer meets some condition. (for example id is "Jack"), using Array.find
const jack = data.find(item => item.id === "Jack");
console.log(jack);
Or to find which customers have a date before "now", using Array.filter
const now = Date.now();
const beforeNow = data.filter(item => item.date < now);
console.log(beforeNow);

storing key value pairs in an array in javascript

I have 2 arrays namely,
configdata = ["assignee", "shortDesc"];
ticketarray = ["Tom","testDesc"];
I want to store the values as a key value pair in another array, something like this:
ticketData = ["assignee":"Tom","shortDesc":"testDesc"];
Kindly note that the array values are dynamic, so I cannot hardcode them.
Is there a way to do so? I am able to achieve the above said requirement but the length always shows 0. This is the code that I am using:
configdata.Incident_Field.forEach(function (k, i) {
this[k] = ticketarray[i];
}, ticketData);
Other people have explained why your code did not work. I am providing another solution using reduce.
const configdata = ["assignee", "shortDesc"];
const ticketarray = ["Tom", "testDesc"];
let ticketData = configdata.reduce((result, value, index) => {
result[value] = ticketarray[index];
return result;
}, {});
console.log(ticketData);
Output:
{
assignee: "Tom",
shortDesc: "testDesc"
}
The below is not a valid structure in JavaScript:
ticketData = ["assignee":"Tom","shortDesc":"testDesc"];
What you need is a JavaScript object. The best you can do is:
Make sure both the array lengths are same.
Associate the key and value by creating a new object.
Use Object.keys(obj).length to determine the length.
Start with the following code:
configdata = ["assignee", "shortDesc"];
ticketarray = ["Tom", "testDesc"];
if (configdata.length == ticketarray.length) {
var obj = {};
for (var i = 0; i < configdata.length; i++)
obj[configdata[i]] = ticketarray[i];
}
console.log("Final Object");
console.log(obj);
console.log("Object's Length: " + Object.keys(obj).length);
The above will give you an object of what you liked, a single variable with all the values:
{
"assignee": "Tom",
"shortDesc": "testDesc"
}

How to generate an key-values inversed object in JavaScript?

update
I have adjusted/corrected the example objects, because they contained an error before.
I have an mapping object that looks like this:
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
};
which allows to map one way i.e. AtoB["love"] yields "amore". I could add an inverse to it manualy i.e.
var BtoA = {
"love": "amore",
"food": "alimenti",
"beer": "Bier"
};
Anyway it troublesome the two objects in sync and I would like to create the BtoA programmatically in Javascript. Is there some sort of function xyz() which yields var BtoA = xyz(AtoB);?
The example above can be extended to include a problem (e.g. if I have too much "beer")
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
as this is not a 1-to-1 mapping. In amateuer math terms It is not an inversible function?
To make things even more complicated I have a recipe for desaster.
var makeStuff = {
"agriculture": "food",
"hops" : {
"water": {
"malt": "beer"},
"malt": {
"water": "beer"}},
"water" : {
"hops": {
"malt": "beer"},
"malt": {
"hops": "beer"}},
"malt" : {
"water": {
"hops": "beer"},
"hops": {
"water": "beer"}}
};
inversing this nested javascript object, seems even more challanging for such an xyz() function. Anyway maybe there is such an xyz() function, then I would be glad to accept this as an answer to this question
Very simple. Following is the code to inverse key, value.
var inverse= (function inv(param){
for(var attr in param) {
if(param.hasOwnProperty(attr)) {
if(typeof param[attr]==="string") {
param[param[attr]] = attr;
delete param[attr];
} else if (Object.prototype.toString.call(param[attr]) === "[object Object]") {
param[attr] = inv(param[attr]);
}
}
}
return param;
});
To get the result into other object, initialize it with empty and assign it. Like
var BtoA = {};
BtoA = inverse(AtoB);
And, The JSON:
var AtoB = {
"love": "amore",
"food": "alimenti",
"beer": "Bier",
"beer": "cerveza",
"beer": "pivo",
"beer": "birra",
"beer": "cerveja",
};
has only three attributes because JSON is a dictionary data structure: new key will replace the old one. So the above JSON will actually be like:
{love: "amore", food: "alimenti", beer: "cerveja"}
So, inverting the above given JSON (AtoB) will result in the inversion of only three properties, and final result will be:
{amore: "love", alimenti: "food", cerveja: "beer"}
The answer from Muhammad imran is effective if the purpose/target is a simple inversion (i.e. no nested object structure; no multiple values).
Obviously that is the best result to be achieved, if no further artifice is created, to cover the fact that the key->value relation in objects are:
keys are unique,
values can be muliple.
Looking at the beer example above it is somewhat regretible that the information is lost in the inversion. Therefore this answer should supplement and enrich and provide a way in which the information can be stored. The way to achieve it is using Javascript Arrays within the resulting inverted object, to allow to store the potentially ambigious new values. as for example.
var BeerAtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer",
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
allowing to translate (de,es,pl/cz,it,pt)"beer" to English would best store
this information in the inverted too
var BeerBtoA = {
"love" : "amore",
"food" : "alimenti",
"beer" : [ "Bier" ,
"cerveza",
"pivo",
"birra",
"cerveja"
]
};
a version in which less information get lost and the multipleness of the original value "beer" is reflected by multipleness of values under the joint, inverted key "beer" now.
To accomplish this I made an enhanced inverting function
function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject; function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject;
}
}
Indeed since equal values can be found in many places of simple and nested object, the result will be an object in which each value will be an Array for two reasons:
Several original object's keys, can have the same value, and therefore (after inverting) more than one value can exists. An Array can store all those multiple new values, hence all information.
While in an nested object, the uniqueness makes every property either a direct value or a subobject, in the inverted object at a key, we can find not only muliple values, but also that at the very same place there are also further nested objects. (For this reason it is lucky that an Javascript Array, as being an Object, does besides its entry allow also for further properties to be attached to it and hence can serve simultaneously as a storage for the multiple values and as a subkey in the nested structure. Such a double purpose of Arrays in the inverted object structure, is unforunatelly hard to show in JSON notation, as the JSON notation does not allow for Arrays with Object Attributes)

Javascript: how to dynamically create nested objects INCLUDING ARRAYS using object names given by an array

Very similar to this question:
Javascript: how to dynamically create nested objects using object names given by an array
Instead of calling
assign(obj, keyPath, value)
example of usage of the previously answer:
var accountinfo = {}
assign(accountinfo, ["name", "addressinfo", "zipcode"], "90210");
That will output:
accountinfo = {name: "", addressinfo: {zipcode:"90210"}};
Now, I'd like to support arrays... in the above example, I'd like to support multiple addressinfo per account. I'd like to say:
assign(accountinfo, ["name", "addressinfo[1]", "zipcode"], "90210");
The result would be:
accountinfo = {name: "", addressinfo: [{},{zipcode:"90210"}]}
var regex = /\[([0-9]+)\]/ will show me the # inside the brackets, but I'm not sure how I'd have to iterate through each element in the array to make sure it exists (and create it if it doesn't).. and the difficult part, support this for each array element submitted as part of the function (I'd like to say :
assign(accountinfo, ["family", "name[3]", "addressinfo[1]", "zipcode"], "90210");
Edit:
Figured it out.
function assign(obj, keyPath, value) {
keyPath = keyPath.split(‘.’);
lastKeyIndex = keyPath.length - 1;
var re = /^(.+?)\[*(\d+)*\]*$/;
for (var i = 0; i < lastKeyIndex; i++) {
key = keyPath[i];
var ind;
var middle = re.exec(key);
key = middle[1];
ind = middle[2];
if (ind) {
if (!(obj[key]))
obj[key] = [];
if (!(obj[key][ind]))
obj[key][ind] = {};
}
if (!(key in obj))
obj[key] = {};
if (ind)
obj = obj[key][ind];
else
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}

Get part of JS object

Not sure if the title of the question is doing it justice, but here is an example. I have object:
var plumber = {
name: 'Mario',
age: 42,
game: 'Super Mario'
}
I am looking for an elegant way of using either jQuery or Undescore to get key and value from this object.
// foo() would be desired elegant function
plumber.foo('name')
#> { name: 'Mario' }
// or even better
plumber.foo(['name','age'])
#> { name: 'Mario', age: 16 }
Thank you!
Underscore.js has _.pick that does exactly that:
var new_obj = _.pick(obj, ['name', 'age']);
I think you could approach this two ways (without either of those two libraries). You can either pass the function an array of keys or you could pass it a variable number of arguments.
fiddle
Using an array
var plumber = {
name: 'Mario',
age: 42,
game: 'Super Mario'
}
var splitObject = function (obj, keys) {
var holder = {};
keys.forEach(function (d) {
holder[d] = obj[d];
})
return holder;
}
var example = splitObject(plumber, ["name", "age"]);
console.log("example #1", example);
Using a variable number of arguments
var variableArguments = function (obj) {
var keys = Array.prototype.slice.apply(arguments).slice(1),
holder = {};
keys.forEach(function (d){
holder[d] = obj[d];
})
return holder;
}
var example2 = variableArguments(plumber, "name", "age");
console.log("example #2", example2);
Underscore.js probably has its own function for this. You'd have to check the documentation.
edit:
pick is probably the most appropriate function, as per the comments above.
Not sure what exactly you're trying to accomplish here, as your first example could be accomplished easily by calling:
plumber.name;
if you were unsure of what the input would be, and needed to pass a variable, you can do it like this:
var arg = 'name';
plumber[arg];
Your second example is a little more complex, for these purposes I'm going to assume you want to be able to pass an arbitrary array of keys and return an object containing the key/value pairs for each one. If you are comfortable with prototyping global objects, you could do this:
Object.prototype.getValuesFromKeys = function (keys) {
var ret = {};
for (var i = 0; i < arr.length; i++) {
ret[keys[i]] = this[keys[i]];
}
return ret;
};
plumber.getValuesFromKeys(['name','age']);
If you want that functionality without prototyping the global Object, you could build it this way instead:
function getValuesFromKeys(obj, keys) {
var ret = {};
for (var i = 0; i < arr.length; i++) {
ret[keys[i]] = obj[keys[i]];
}
return ret;
};
getValuesFromKeys(plumber, ['name','age']);

Categories

Resources