I have an an object I need to transform into a different format
The original:
{"module1":{"mod1":[{"hours":10},{"weeks":2},{"days":15}]},
"module2":{"mod2":[{"cars":1},{"cats":5},{"people":4}]},
}
the desired result :
{"module1":"/mod1/10/2/15","module2":"/mod2/1/5/4" }
Here is my attempt (sorry im still learning this)
function(newUrl){
var encodedUrl = {};
var tempString = "";
console.log(JSON.stringify(newUrl));
for (var p in newUrl) {
tempString = "/" + Object.keys(newUrl[p]);
for (var j in newUrl[p]){
_.each(newUrl[p][j], function(obj){
tempString += "/" + _.values(obj);
});
encodedUrl[p] = tempString;
console.log(encodedUrl);
}
}
}
So, I think i was able to make the string correctly. Hoever it only seems to be working the first time around. it's logging in a weird pattern
Object {module1: "/mod1/10/2/15"}
Object {module1: "/mod1///"}
Object {module1: "/mod1///", module2: "/mod2/1/5/4"}
I think i have something wrong in my logic parsing this, I cannot pinpoint it though. Would love a second pair of eyes to help. Thanks!
You have to loop over the properties of each object in turn, extracting the property names and values. There is also an array, so you have to loop over that too.
4 nested loops.
function transformObj(obj) {
var tObj, tStr, tArr, aObj, result = {};
// For each own property in the object, e.g. 'module1'
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
// Transorm the value
tObj = obj[p];
tStr = '';
// For each property in that object, e.g. 'mod1'
for (var q in tObj) {
if (tObj.hasOwnProperty(q)) {
tStr = '/' + q;
tArr = tObj[q];
// for each member of the array
for (var i=0, iLen=tArr.length; i<iLen; i++) {
aObj = tArr[i]
// for each property of each member, e.g. hours, weeks days
for (var r in aObj) {
if (aObj.hasOwnProperty(r)) {
tStr += '/' + aObj[r];
}
}
}
}
}
// Assign transformed value to result object
result[p] = tStr;
}
}
return result;
}
var obj = {"module1":{"mod1":[{"hours":10},{"weeks":2},{"days":15}]},
"module2":{"mod2":[{"cars":1},{"cats":5},{"people":4}]},
};
console.log(JSON.stringify(transformObj(obj)));
// {"module1":"/mod1/10/2/15","module2":"/mod2/1/5/4"}
You can replace the for..in and hasOwnProperty parts with Object.keys(...).forEach(...) to reduce the code a bit, but likely increase the complexity.
Note that the order that properties are returned by for..in and Object.keys will be the same but perhaps not necessarily as you expect, and may be different from browser to browser, so you can only expect consistent results when each object has one property.
There's probably a shorter way, but it seems that your object is sort of complex.
Object.keys(obj).reduce(function(newObj, key, index) {
var module = obj[key];
var moduleKey = 'mod' + (index+1);
var arr = module[moduleKey].map(function(o){return o[Object.keys(o)[0]]}).join('/');
newObj[key] = "/" + moduleKey + "/" + arr
return newObj
}, {});
Related
I have an array of allowedFields based on the names of the keys from a JSON array generated from a form.
A number of the retrieved fields are not required at this stage and therefore should not go through the validation process, therefore I want to match the values of the JSON array with the values of the allowedFields array
Returned JSON from form
{"reference":"sdfsdfsdfsd",
"start_date":"04/22/2014",
"end_date":"05//2014",
"status":"1","frequency":"M",
"day":"sat",
"contract_type":"S",
"notice_period":"1M"}
allowedFields = array(
reference,
start_date,
end_date,
contract_type
)
Basically I need to strip out any fields that are not listed in the allowedFields javascript array
1) Parse the JSON to an object.
var obj = JSON.parse(json);
2) Ensure that you've defined your array correctly.
var allowedFields = ['reference','start_date','end_date','contract_type'];
3) Loop over the object and if the key is not in the array delete it.
for (var k in obj) {
if (allowedFields.indexOf(k) < 0) delete obj[k];
}
4) Stringify your object back to JSON.
var str = JSON.stringify(obj);
Output
{"reference":"sdfsdfsdfsd","start_date":"04/22/2014","end_date":"05//2014","contract_type":"S"}
Fiddle
var all = {"reference":"sdfsdfsdfsd",
"status":"1"};
var allowedFields = ['reference']; // note quote marks to create strings
function filter(data, allowed) {
var filtered = {};
for(var id=0; id < allowed.length; ++id) {
var allowedField = allowed[id];
if(data.hasOwnProperty(allowedField)) {
filtered[allowedField] = data[allowedField];
}
}
return filtered;
}
console.log(filter(all, allowedFields));
>> [object Object] {
>> reference: "sdfsdfsdfsd"
>> }
Demo
underscore.js solution:
_.pick(obj,allowedFields)
http://underscorejs.org/#pick
demo
There is also _.omit(obj,string|string[]) that does the opposite.
underscore.js is extremely useful and I use it quite a bit, but you can also pick just the tools you need and include those in your code. The library is quite optimized and there is no need to write your own.
Here is the implementation (from here)
_.pick = function(obj, iterator, context) {
var result = {};
if (_.isFunction(iterator)) {
for (var key in obj) {
var value = obj[key];
if (iterator.call(context, value, key, obj)) result[key] = value;
}
} else {
var keys = concat.apply([], slice.call(arguments, 1));
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
if (key in obj) result[key] = obj[key];
}
}
return result;
};
I'm currently doing some coursework for university. I am trying to copy an individual value of an old array to a new array, then setting the old arrays value to 0. Obviously if i just assign the value to the new array, then alter the old arrays value, it will overwrite the new array too.
I am not allowed to use the function splice().
here is my code:
function rankedScores(web, pattern) {
var v = urlScores(web, pattern);
var sorted = [];
var maxIndex = 0;
while (sorted.length < v.length) {
for (var i = 0; i < v.length; i += 1) {
if (v[i].score > v[maxIndex].score) {
maxIndex = i
}
}
sorted[sorted.length] = v[maxIndex];
v[maxIndex].score = 0;
maxIndex = 0;
}
alert(sorted[0].url + ' ' + sorted[0].score)
alert(sorted[1].url + ' ' + sorted[1].score)
alert(sorted[2].url + ' ' + sorted[2].score)
}
If i do this it returns the correct URL value, but all the score values are 0.
Any ideas on how i can stop the arrays from pointing to the same memory location?
Ive tried using a for loop as ive seen this does a shallow copy, but it didnt work
Cheers.
Replace:
sorted[sorted.length] = v[maxIndex];
v[maxIndex].score = 0;
with:
// ...
var clone = {};
for(var i in v[maxIndex])
clone[i] = v[maxIndex][i];
sorted[sorted.length] = clone;
v[maxIndex].score = 0;
// ...
Of course, you haven't stated how deep your objects are - I assume they are simple key:value maps but this should be enough to steer you in the right direction.
I have a js object which looks like this:
var detailsArray = [
{a0 :1,
b0 :'A'},
{a1 :2,
b1 :'B'},
{a2 :3,
b2 :'C'},
{a3 :4,
b3 :'D'}];
This is how the object is created from the server side. On the client side I want to retrieve the value of all 'a's and add them to an array. The problem is that the variable name is changing depending on the index number. I have tried using underscore.js to do something like this:
var variableA = new Array();
for(var i = 0;i<detailsArray.length;i++){
var temp = 'a' + i;
variableA[i] = _.pluck(detailsArray,temp);
}
But this does not work. Can anyone tell how to get the values??
There is two ways for accessing properties of object in javascript : using the dot like you just done, or using the array syntax style.
var obj = {'a':5};
obj.a
obj['a']
So with your code, this would give this :
var variableA = new Array();
for(var i = 0;i<detailsArray.length;i++){
variableA[i] = detailsArray[i]['a' + i];
}
With underscore, you could do:
_.reduce(_.map(detailsArray, function(o, i) {
return o['a' + i];
}), function(a, b) {
return a + b;
});
And with native JS in newer browsers:
detailsArray.map(function(o, i) {
return o['a' + i];
}).reduce(function(a, b) {
return a + b;
});
You can also do it like that:
for (var i = 0; i < detailsArray.length; i++)
alert(eval("detailsArray[" + i + "].a" + i));
The code I provide will alert all the values corresponding to as in the json array, but obviously you can do whatever you want with the values obtained.
Here I am counting that all the keys will be of the kind a smth, but I suppose this is a safe assumption.
here's one possible implementation
var tmp = [];
for (var i = 0; i < detailsArray.length; i++) {
var obj = detailsArray[i]; // current object at index
for (var props in obj) { // iterate properties in current object
if (props.charAt() == "a") { // if starts with a....
tmp.push(obj[props]); // add value to array
break; // stop the property iteration and move to next object
}
}
}
console.log(tmp); // [1,2,3,4]
suppose I do..
var arr = Array();
var i = 3333;
arr[i] = "something";
if you do a stringify of this array it will return a string with a whole bunch of undefined numeric entries for those entries whose index is less than 3333...
is there a way to make javascript not do this?
I know that I can use an object {} but I would rather not since I want to do array operations such as shift() etc which are not available for objects
If you create an array per the OP, it only has one member with a property name of "333" and a length of 334 because length is always set to be at least one greater than the highest index. e.g.
var a = new Array(1000);
has a length of 1000 and no members,
var a = [];
var a[999] = 'foo';
has a length of 1000 and one member with a property name of "999".
The speedy way to only get defined members is to use for..in:
function myStringifyArray(a) {
var s = [];
var re = /^\d+$/;
for (var p in a) {
if (a.hasOwnProperty(p) && re.test(p)) {
s.push(a[p]);
}
}
return '' + s;
}
Note that the members may be returned out of order. If that is an issue, you can use a for loop instead, but it will be slower for very sparse arrays:
function myStringifyArray(a) {
var s = [];
var re = /^\d+$/;
for (var i=0, iLen=a.length; i<iLen; i++) {
if (a.hasOwnProperty(i)) {
s.push(a[i]);
}
}
return '' + s;
}
In some older browsers, iterating over the array actually created the missing members, but I don't think that's in issue in modern browsers.
Please test the above thoroughly.
The literal representation of an array has to have all the items of the array, otherwise the 3334th item would not end up at index 3333.
You can replace all undefined values in the array with something else that you want to use as empty items:
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] == 'undefined') arr[i] = '';
}
Another alternative would be to build your own stringify method, that would create assignments instead of an array literal. I.e. instead of a format like this:
[0,undefined,undefined,undefined,4,undefined,6,7,undefined,9]
your method would create something like:
(function(){
var result = [];
result[0] = 0;
result[4] = 4;
result[6] = 6;
result[7] = 7;
result[9] = 9;
return result;
}())
However, a format like that is of course not compatible with JSON, if that is what you need.
I have a list of objects all named in this fashion:
var p1 = {};
var p2 = {};
p1.name = "john";
p1.hobby = "collects stamps";
p2.name = "jane";
p2.hobby = "collects antiques";
I know how to loop through p1 and p2 to collect the properties, provided I know how many of these p object literals there are. Here's my problem, I don't always know how many of these p object literals there will be. Sometimes it goes up to p2, sometimes it goes up to p20.
Is there a way to loop through objects if I know they all share the same prefix?
Edit: I can't change how I'm getting the list of objects. It's given to me in that format...
If we make the following assumptions:
The objects are global
The number suffixes are sequential
...then the following works:
for (var i = 1; window["p" + i] !== undefined; i++) {
console.log(window["p" + i]); // loop over each object here
}
You should have them in an Array referenced by a single variable.
var p = [];
p.push({
name:"john",
hobby:"collects stamps"
}, {
name:"jane",
hobby:"collects antiques"
});
Then you'd loop the Array, and enumerate each object...
for( var i = 0; i < p.length; i++ ) {
for( var n in p[i] ) {
console.log( p[i][n] );
}
}
EDIT:
It seems from a comment that these may be arriving as individual variable.
If they're global variables, and if they always have the same p1 naming, then you can access them as properties of the global window object.
var obj;
for( var i = 1; obj = window['p' + i]; i++ ) {
if( typeof obj === 'object' ) {
for( var n in obj ) {
console.log( obj[n] );
}
}
}
This loop will run until a p(n) global returns a falsey value.
So as long as a truthy value is found, and its typeof is 'object', you'll iterate that object.
If you have all your data stored in a variable , or a few variables you can push it into the array.
var data = "....JSON";
var a = [];
a.push(data);
Push keeps adding stuff into the array in basic sense.
You could also pop to remove the last pushed data.
Take a look at the other methods here:
http://www.w3schools.com/jsref/jsref_obj_array.asp
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
Why don't you just store them all in one top-level object literal? It will make it easier to enumerate through them.
EG:
var MyObj = {
p1: {},
p2: {}
};
etc..
[edit]
If they are local vars, are you can't change the format of this data, you might have to use eval.
Don't shoot me:
var p1 = {};
var p2 = {};
p1.name = "john";
p1.hobby = "collects stamps";
p2.name = "jane";
p2.hobby = "collects antiques";
var found = true, c = 1;
while(found) {
try {
var obj = eval('p' + c);
c++;
console.log(obj);
} catch(e){
found = false;
}
}
I don't suggest using this, I suggest changing the format of the data you are receiving, but this is one possible solution.