How to convert object key dot notation to object tree in javascript - javascript

I have a form and I want to send it to an ajax endpoint, for this I have this helper method:
$.fn.serializeFormToObject = function() {
//serialize to array
var data = $(this).serializeArray();
//add also disabled items
$(':disabled[name]', this)
.each(function() {
data.push({ name: this.name, value: $(this).val() });
});
//map to object
var obj = {};
data.map(function(x) { obj[x.name] = x.value; });
return obj;
};
The problem is that I have dot notation in some of the names of my form (using MVC strong typed model)
So I have this object as result:
Task.Addresses.Box:""
Task.Addresses.City:"Limal"
Task.Addresses.Country:"Belgique"
Task.Deadline:"1/10/2017 12:18:18 PM"
Task.TaskSourceId:"1"
And the result expected would be:
{ Task : { Addresses: { Box: "", City: "Limal", Country: "Belgique"}, Deadline: "1/10/2017 12:18:18 PM", TaskSourceId:"1" } }
I use the lodash library but after some hours I cannot find a way to do this expected result.
Can someone provide me a working javascript helper to give the expected nested object?
Edit:
For duplicated question, the other question does not ask about combined nested object together
After the answer of #ori-drori, This code is working as expected:
$.fn.serializeFormToObject = function() {
//serialize to array
var data = $(this).serializeArray();
//add also disabled items
$(':disabled[name]', this)
.each(function() {
data.push({ name: this.name, value: $(this).val() });
});
//map to object
var obj = {};
data.map(function (x) { obj[x.name] = x.value; });
var objNested = {};
_.forEach(obj, function (value, key) { _.set(objNested, key, value) });
return objNested;
};

Iterate the data using Array#forEach, and assign the value to the path (name) using _.set():
data.forEach(function(x) { _.set(obj, x.name, x.value) });
I don't have you're original data, so here is a demo using the key-values you provided.
var fields = {
"Task.Addresses.Box": "",
"Task.Addresses.City": "Limal",
"Task.Addresses.Country": "Belgique",
"Task.Deadline": "1/10/2017 12:18:18 PM",
"Task.TaskSourceId": "1"
};
var obj = {};
_.forEach(fields, function(value, key) {
_.set(obj, key, value);
});
console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Related

Javascript - Split and return array in multiple arrays or string

I have a json like this:
{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"}]}}
... and I dynamically fetch the values and create a variable this:
var existingParams = [
"name",
"updated"].filter(field => getBody.search[field]);
var sqlVal = existingParams.map(field => {
if (field === 'name') {
function getValues(item, index) {
var getVal = [item.tag];
return "%" + getVal + "%";
}
console.log(name.map(getValues));
return name.map(getValues);
} else {
return getBody.search[field];
}
})
For the above example I get for sqlVal:
console.log(sqlVal);
[ [ '%Peter%' ], '2018-11-07' ]
... which is fine.
BUT, if I have two values:
{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}}
... I'm getting this structure:
[ [ '%Peter%', '%Jack%' ], '2018-11-07' ]
... but what I need is sth like:
[ '%Peter%', '%Jack%', '2018-11-07' ]
... or:
[ ['%Peter%'], ['%Jack%'], '2018-11-07' ]
And in case of further e.g. 3 names:
{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}
... I need sth like:
[ '%Peter%', '%Jack%', '%Maria%', '2018-11-07' ]
... or:
[ ['%Peter%'], ['%Jack%'], ['%Maria%'], '2018-11-07' ]
... and so on
How do I need to adjust the above query to get this?
If I understand your question correctly, then this problem can be solved via the Array#reduce() method.
The general idea with this approach is to transform your input object to an array - the reduce operation can be used to do this, with the special-case rule of "flattening" the nested value on the name key into the final result:
var input = {"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}}
var result = Object
.entries(input.search)
.reduce(function(result, entry) {
const key = entry[0]
const value = entry[1]
if(key === 'name') {
// When the 'name' key is encountered, handle the value
// differently, by addting the items of this value array
// to the result
value.forEach(function(item) {
result.push('%' + item.tag + '%')
})
}
else {
// Append values for other keys directly to the result
result.push(value)
}
return result
}, [])
console.log(result )
You could simply use Object.values + reduce for something like this:
const json = { "search": { "updated": "2018-11-07", "name": [{ "tag": "Peter" }, { "tag": "Jack" }, { "tag": "Maria" }] } }
const result = Object.values(json.search).reduce((r,c) =>
(Array.isArray(c) ? r.push(...c.map(({tag}) => `%${tag}%`)) : r.push(c), r),[])
console.log(result)
If the order is important (names first then date) you could use reverse:
const json = { "search": { "updated": "2018-11-07", "name": [{ "tag": "Peter" }, { "tag": "Jack" }, { "tag": "Maria" }] } }
const result = Object.values(json.search).reverse().reduce((r,c) =>
(Array.isArray(c) ? r.push(...c.map(({tag}) => `%${tag}%`)) : r.push(c), r),[])
console.log(result)
First of all you did not provide a Minimal, Complete, and Verifiable example so it is quite hard for me to figure out where you are running into issues. For example, you are referencing existingParam but nowhere are they defined. This is key to understanding the problem because all of the code that you posted is heavily invested in the values and format of this value.
Second, how are you parsing the JSON? With the standard JSON#parse function you would get back an object with the same structure as your provided JSON. However, you are either not using this or you are mutating the object after it was parsed into a new format. Either way, the object that JSON#parse returns for the provided JSON is not an array and therefor you cannot use Array#map on it.
For the sake of being productive though I am going to try and explain how to do things.
JSON:
let data1 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"}]}}',
data2 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}} ',
data3 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}';
Now that we have our JSON data we need to parse it and store it as a JSON object. To do so I am going to create a function; this way the data can be passed to the same function and handled the same way but the implementation will stay the same. Also, since we are only looking at the values in the search property we are going to go ahead and jump right into it.
Parse the JSON:
function parseResponse (response) {
let parsedResponse = JSON.parse(response);
parsedResponse = parsedResponse['search'];
}
Now that we have our function that takes our response and parses it we can then begin to sort through it to find and isolate the parts that we want. In this case we will add some code to loop through our properties and find the updated and name properties.
function parseResponse (response) {
let parsedResponse = JSON.parse(response);
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
// do stuff with 'updated'
}
if (prop === 'name') {
// do stuff with 'name'
}
}
}
Because we want to return a result we are going to add a variable updated and names which will hold the values that we pull out of the string until we are ready to return them. Now that we have our loop and our temporary variables we can go ahead and pull the updated value out of our data and place it in the updated variable.
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
// do stuff with 'name'
}
}
}
With our updated value squared away we can jump into our names. Since you listed the format ['%name%', '%name%', '%name%'] first I am going to go ahead and show you how to do it this way. Here we are going to grab the property name, iterate through the names, grab the tag property, and then add the %s before pushing it to our names temporary variable.
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
for (let i = 0; i < parsedResponse[prop].length; i++) {
let name = parsedResponse[prop][i].tag;
name = '%' + name + '%';
names.push(name);
}
}
}
}
With everything in place all that is left is to assemble the result. To do so we are going to flatten the array of names, add them to the array, and then add the updated value to the end before returning it. To flatten the array we are going to use the spread operator.
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
for (let i = 0; i < parsedResponse[prop].length; i++) {
let name = parsedResponse[prop][i].tag;
name = '%' + name + '%';
names.push(name);
}
}
}
return [...names, updated];
}
With all of that set we can go ahead and call parseResponse() with data1, data2, or data3 and get back a response that looks like so:
let data1 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"}]}}',
data2 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"}]}} ',
data3 = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}';
function parseResponse (response) {
let parsedResponse = JSON.parse(response),
updated = '',
names = [];
parsedResponse = parsedResponse['search'];
for (let prop in parsedResponse) {
if (prop === 'updated') {
updated = parsedResponse[prop];
}
if (prop === 'name') {
for (let i = 0; i < parsedResponse[prop].length; i++) {
let name = parsedResponse[prop][i].tag;
name = '%' + name + '%';
names.push(name);
}
}
}
return [...names, updated];
}
console.log(parseResponse(data1));
console.log(parseResponse(data2));
console.log(parseResponse(data3));
Spread operator can be used to flatten the result :
var obj = {"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}
var arr = [...obj.search.name.map(n => `%${n.tag}%`), obj.search.updated]
console.log( arr )
Another alternative could be to extract during parsing :
var arr = [], json = '{"search":{"updated":"2018-11-07","name":[{"tag":"Peter"},{"tag":"Jack"},{"tag":"Maria"}]}}'
JSON.parse(json, (k, v) => v.trim && arr.push(k === 'tag' ? `%${v}%` : v))
console.log( arr )

Returning an object with map.call

This works:
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container .title'), function(e) {
return e.querySelector('.title').textContent;
});
}
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container .title a'), function(e) {
return e.querySelector('.title a').href;
});
}
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container img'), function(e) {
return e.querySelector('img').src;
});
}
.. however, I want to return all results as a single object, but this doesn't work because I can't figure how to get the index:
evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.container'), function(e) {
var data = {};
data[index].title = e.querySelector('.title').textContent;
data[index].link = e.querySelector('.title').href;
data[index].image = e.querySelector('img').src;
// return all results in a single object with each containing title, link and image
return data;
});
}
This is the complete function:
new Nightmare(
.goto('https://www.example.com')
.insert('form[id="gbqf"] input[id="gbqfq"]', 'keyword here')
.click('form[id="gbqf"] button[id="gbqfb"]')
.wait('.sh-sr__shop-result-group')
.visible('.sh-sr__shop-result-group')
.evaluate(function() {
return Array.prototype.map.call(document.querySelectorAll('.sh-sr__shop-result-group .psli'), function(data, e) {
data.push({
title: e.querySelector('.pslmain h3').textContent,
thumb: e.querySelector('.pslimg img').src,
link: e.querySelector('.pslmain h3 a').href,
price: e.querySelector('.pslline .price').textContent,
stars: e.querySelector('span._Ezj div').getAttribute('aria-label'),
ratings: e.querySelector('a.shop__secondary.sh-rt__product').textContent,
//features: e.querySelector('._Lqc .shop__secondary _MAj').innerHTML,
//description: e.querySelector('._Lqc').textContent
});
return data;
});
})
.end()
.then(function(result) {
console.log(result);
})
.catch(function(error) {
console.error('Search failed:', error);
});
I am not sure what shape you want your final object to be . but this seems as close to first implementation as possible .
function evaluate(containerName){
return Array.prototype.reduce.call(document.querySelectorAll(containerName), function(acc , e){
acc.push({
title: e.querySelector('.title').textContent,
link: e.querySelector('.title a').href,
image: e.querySelector('img').src
})
return acc
} , [] )
}
returns an array of objs corresponding to containers indexs .
You may iterate over document.querySelectorAll('.container') before the map and build an array with indexes, like:
var kvArray = [{index:0, value:document.querySelectorAll('.container')[0]}, {index:2, value:20}, {index:3, value: 30}];
var reformattedArray = kvArray.map(function(obj){
var rObj = {};
rObj[obj.key] = obj.valor;
return rObj;
});
And use your object key to access the rObj correspondent place.
Anyway from the docs, seems to be that you can get the index from the callback function. Please take a look to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map?v=control
var new_array = arr.map(callback[, thisArg])
Parameters
callback
Function that produces an element of the new Array, taking three arguments:
currentValue
The current element being processed in the array.
index
The index of the current element being processed in the array.
array
The array map was called upon.
thisArg
Optional. Value to use as this when executing callback.
Hope it helps.

How do access a json object 0 using a javascript

I pretend to do console.log of the holidays variable appear in the list and not as an array. How do I get this information as a simple object?
My json:
[{
"2016-10-10":[{"name":"Columbus Day","country":"US","date":"2016-10-10"}],
"2016-10-31":[{"name":"Halloween","country":"US","date":"2016-10-31"}]
}]
My app:
app.factory('calendario', function($http) {
return{
getJobs : function() {
return $http({
url: '/app/components/home/controller/test_calendar.json',
method: 'GET'
})
}
}
});
controller:
var holidays = [];
calendario.getJobs().success(function(data){
holidays.push(data[0]);
});
$scope.setDayContent = function(date) {
console.log(holidays);
};
Example:
I want this
I don't want this
You want to flatten it, so, Nick is right, do not use the array, use dict/hash instead. In case your functionality will suffer you can use some workaround:
var holidays = [{
"2016-10-10":[{"name":"Columbus Day","country":"US","date":"2016-10-10"}],
"2016-10-31":[{"name":"Halloween","country":"US","date":"2016-10-31"}]
}];
var flatten_holidays = holidays.reduce(function(elem){return elem});
I do not feel like it is specifically AngularJS question, more like common JavaScript instead.
For your example it would be:
var holidays = [];
calendario.getJobs().success(function(data){
holidays.push(data[0]);
});
$scope.setDayContent = function(date) {
console.log(holidays.reduce(function(elem){return elem}));
};
or, if the preference is to use dict/hash:
var holidays = {};
calendario.getJobs().success(function(data){
//if data is: {"2016-10-10":[{"name":"Columbus day","country":"US","date":"2016-10-10"}]}
var key = Object.keys(data)[0];
var value = data[key];
holidays[key] = value;
});
$scope.setDayContent = function(date) {
console.log(holidays);
};
This question is to easy ... no?
var holidays = [];
calendario.getJobs().success(function(data){
holidays=data[0];
});
$scope.setDayContent = function(date) {
console.log(holidays);
};
Instead of a list use a map
holidays = {}
Then put the items in the map with key the date and value the array:
holidays[data[0].date] = data[0].days;
But it looks like you already have the map, so probably you can just save data directly instead of pushing it in a list:
var holidays = {};
calendario.getJobs().success(function(data){
holidays=data;
});
$scope.setDayContent = function(date) {
console.log(holidays);
};

Retrieving name of object in firebase

I have a list / an array of objects organized numerically which i consider the object name as an 'id'. I am using firebase and I can't figure out how to grab the id number itself. Any help would be great
//js
myFirebaseRef.once("value", function(snapshot) {
var list = snapshot.val();
$scope.items = list
console.log($scope.items)
})
//log $scope.items
[1: Object, 2: Object, 3: Object, 4: Object, 5: Object, 6: Object, 7: Object //...
with angular.forEach like that you can do something near :
angular.forEach($scope.items,function(key,value){
//the key will contain your id
console.log(key);
})
Is this what you're looking for?
myFirebaseRef.once("value", function(snapshot) {
//var list = snapshot.val();
snapshot.forEach(function(itemSnapshot) {
console.log(itemSnapshot.key); // if you're using 2.x that is key()
});
})
Like this. You use obj[key] = value; to set the id with a variable.
function update(node,key,value){
var ref = firebase.database().ref('/');
var obj = {};
obj[key] = value;
ref.child(node).update(obj)
.then(function() {
console.log('Update Ran Successfully');
});
}
This is a real live example, using react and axios to retrieving the name of the object in firebase :
componentDidMount() {
axios.get('/uri.json')
.then(res => {
let fetchedData = [];
for (let key in res.data) {
fetchedData.push({
...res.data[key],
id: key //The key here holds the object's name !
});
}
});
}

how to get left side data of a json dynamically

I have a json which is like
{
"Payload":[{
"PrevYr":"21333",
"CurYr":"123454"
},{
"PrevYr":"234333",
"CurYr":"45546"
},{
"PrevYr":"3563",
"CurYr":"67854"
}]
}
Now in my code i read this json using
$.getJSON("readJson.json", function (data) {}
I need to read the json and create another json with some changes on it. But i can't read the "CurYr" and "PrevYr". I mean not their values. But them. So if there is 2014 and 2015 in place of curyr and prevyr i can get them also. I need to read the left side. Please help..
Try something like this,
var test = {
"Payload":[{
"PrevYr":"21333",
"CurYr":"123454"
},{
"PrevYr":"234333",
"CurYr":"45546"
},{
"PrevYr":"3563",
"CurYr":"67854"
}]};
$.each(test.Payload,function(key,val){
$.each(val,function(key1,val1){
alert(key1);
});
});
or
var keySet = Object.keys( test.Payload[0] );
alert( keySet[0]);
alert( keySet[1]);
you can use Object.keys( obj );
try
$.getJSON("readJson.json", function (data) {
console.log( Object.keys(data.Payload[0]));
}
try this:
var obj = {
"Payload":[{
"PrevYr":"21333",
"CurYr":"123454"
},{
"PrevYr":"234333",
"CurYr":"45546"
},{
"PrevYr":"3563",
"CurYr":"67854"
}]
}
var objKeys = Object.keys(obj);
objKeys.forEach(function(key){
// This returns "Payload"
var _innerKeys = Object.keys(obj[key]);
// This returns "0,1,2" since obj[key] is an array.
_innerKeys.forEach(function(k){
// This will return "PrevYr, CurYr"
var _innerProp = Object.keys(obj[key][k]);
console.log(_innerProp);
})
console.log("Inner Keys: ", _innerKeys);
})

Categories

Resources