This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 7 years ago.
I tried to make it by myself, but I don't know if this is possible
function smth(){
var temp = [];
for(var i = arguments.length -1; i > 2; i-=1){
var temp2 = [];
temp.push(arguments[i]);
temp2.push(temp);
temp = temp2;
console.log(temp);
}
// I need to get array in this form
var something = item['collections']['0']['name'];
}
smth('collection','0','name');
edit:
Okay, maybe I haven't given you enough information.
I've got a JSON object, and I'm making a filter function, and I'd like to make it more reusable because now I have hard-coded item.collections[0].name,
and sometimes I need use item.parameters.name, and I will use it few a more times
$scope.$watch(name, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.productsChucks = myFilter(array, function(item) {
//console.log(item['collections']['0']['name']);
if (item.collections[0].name == $scope[compareWith]) {
return item;
}
});
}
});
I think you stated your question completely wrong, IMHO it's a typical XY problem https://meta.stackexchange.com/a/66378
Anyway, based on your edit, what I think you really want is to get some nested properties of an object using a string in form "item.parameters.name".
The simplest way to do this is to use some kind of a helper library, eg. lodash:
_.get(item, 'parameters.name') // returns item.parameters.name
_.get(item, 'collections[0].name') // returns item.collections[0].name
Using that your code will look similar to this:
// path is a string given as the parameter to the filter
if (_.get(item, path) === $scope[compareWith]) {
return item;
}
Your function smth will now take only one argument:
smth('collection[0].name');
More information about lodash can be found here https://lodash.com/docs#get
If you think you don't need whole lodash you can implement this single function by yourself, take a look here https://stackoverflow.com/a/6491621/704894
If you need to access it this way:
var something = item['collections']['0']['name'];
Then it's not an array, but actually an object accessed with index notation. You can do it this way:
function smth() {
var temp = {};
var root = temp;
for (var i = 0; i < arguments.length; i++) {
temp[arguments[i]] = {};
temp = temp[arguments[i]];
}
return root;
}
console.log(JSON.stringify(smth('collection', '0', 'name'), null, 2));
Related
I am just curious about this.
Let's say I have an array of objects and I create 1 object, lets name the array of objects items and the object item.
I want to get a particular item in my array of items by using the following code:
//gets an item base on ID
function get_item(td){
var item = undefined;
$.each(items, function(i, val) {
if(val.item_id == td){
item = val;
}
});
return item;
}
The get_item() basically gets an object matched with the supplied id.
So my question is this. What if I changed the properties of item will it also changed the properties of an object associated with it within the array?
Thank you very much!
What if I changed the properties of item will it also changed the properties of an object associated with it within the array?
Yes.
Objects are not copied. Instead, references to the objects are passed around. Simplest example:
var a = [];
var b = a;
b.push(1);
console.log(a); // logs [1]
Many object-oriented programming languages work like this.
The value of the object inside the array will also change because it's a reference. If you want more information I highly recommend reading Objects and Prototypes.
If you don't want it to change then you should use something like lodash's _.clone() function.
Also you could use filter to get the object:
function get_item(td){
return items.filter(function(item) {
return item.id === td;
})[0];
}
You can update you function to:
var data= array();
function get_item(propertyValue, propertyName){
var retval;
for(var i = 0; i < data.length; i++){
if(data[i][propertyName]==propertyValue){
retval = data[i];
break;
}
}
return retval;
}
Use it
var item1 = get_item(1,"id");
var item2 = get_item("john","name");
This question already has answers here:
Javascript, Transforming object with underscore (or not)
(2 answers)
Closed 7 years ago.
Trying to change an object to prepare it for send by taking the array inside and turning it into an object with the items in the array being keys with values of true.
It starts out looking like -
{"state":["Saved","Published"],"discipline":["Marketing"]}
So the end result would look like
{"state":{"Saved":true,"Published":true},"discipline":{"Marketing":true}}
So it just looks at that array and changes it to an object with values of true. I am trying to use underscore but plain js would work just fine.
Here is my attempt, is there a better way to do this, maybe with underscore? -
function transformOutputToFilterModel(model) {
var transformedObj = _.map(model, function(obj) {
return obj.reduce(function(obj, k) {
obj[k] = true;
return obj;
}, {})
});
return transformedObj;
}
Thanks for reading!
Edit - sorry I had marked an answer wihtout realizing it wasn't exactly corret. I have this function -
var transformedObj = _.map(model, function(obj) {
return obj.reduce(function(obj, k) {
obj[k] = true;
return obj;
}, {});
});
this does format the inside object correctly, however it is pulling off the outer keys - see here : https://jsfiddle.net/d0kjctfh/3/
Those outer objecst should have the keys of state and discipline like this -
{"state":{"Saved":true,"Committed":false,"Published":false},"discipline":{"Marketing":true}};
Thanks!
You need to do it in several steps:
_.mapObject(model, function(vals) {
return _.object(vals, _.map(vals, function() { return true; }));
} );
The map just makes a list of trues the right length, the _.object changes ["Saved","Published"] into {"Saved":true,"Published":true} and the _.mapObject applies it to every field.
I think _.map will always return an array so its not the best solution. Try the code bellow.
var t = {"state":["Saved","Published"],"discipline":["Marketing"]},
newObj = {};
_.each(t, function(obj, index) {
newObj[index] = {};
if(_.isArray(obj)){
var toReturn = {};
_.each(obj, function(index,item){
toReturn[index] = true;
});
newObj[index] = toReturn;
}
});
console.log(newObj);
No need for underscore; Array.map() is native Javascript. However, map returns an array, which isn't what you want. Try reduce, accumulating the results into an (initially) empty object:
var input = {"state":["Saved","Published"],"discipline":["Marketing"]};
var toObject = function(arr) {
return arr.reduce(function(prev,curr){
prev[curr]=true;
return prev;
},{});
}
console.log(toObject(input.state));
var input = {"state":["Saved","Published"],"discipline":["Marketing"]};
function childToObj(arr) {
return arr.reduce(function(prev,curr){
prev[curr]=true;
return prev;
},{});
}
for (var key in input) {
if (input.hasOwnProperty(key)) {
input[key] = childToObj(input[key]);
}
}
console.log(input);
console.log(JSON.stringify(input));
Sometimes the easiest way is the classic way:
function prepareObject(obj){
var i, e, o = {};
for(e in obj){
if(obj.hasOwnProperty(e)){
o[e] = {};
for(i = 0; i <= obj[e].length - 1; i++) {
obj[e][obj[e][i]] = true;
}
}
}
return o;
}
Optimized / minify
window.prepareObject=function(b){var c,a,d={};for(a in b)if(b.hasOwnProperty(a))for(d[a]={},c=0;c<=b[a].length-1;c++)b[a][b[a][c]]=!0;return d};
This question already has answers here:
Copy array by value
(39 answers)
Closed 8 years ago.
I have an array example fruit . I'd like to copy it as array fruits2, without keeping reference.
As in the following example reference is kept so fruits is modified.
var fruit = function (name){
this.name = name;
}
var fruits = [];
fruits.push(new fruit('apple'));
fruits.push(new fruit('banana'));
fruits.push(new fruit('orange'));
var fruits2 = fruits;
fruits2.length = 0;
console.log(fruits);
http://jsfiddle.net/vkdqur82/
Using JSON.stringify and JSON.parse does the trick but the objects in fruits2 are not any longer of type fruit but are of general type object
var temp = JSON.stringify(fruits);
var fruits2 = JSON.parse(temp);
I would like to know an alternative approach which would keep inner object of fruit.
Use slice: var fruits2 = fruits.slice(); should do it.
Your jsFiddle, modified
See also: MDN
**Edit. I was a bit lazy, let's correct my answer to make up for that.
For an Array of just values slice is perfect. For an Array of objects or arrays or a mix of values/objects/arrays, the Array and Object elements of the Array to clone need cloning too. Otherwise they will be references to the original arrays or objects (so: not copies) and a change of one [of these references of arrays or objects] will be reflected in all 'clones' containing a reference to it.
To clone an Array of Arrays/Objects/mixed values Array.map is your friend. There are several methods to think of:
creating a new instance with old data
var fruits1 = fruits.map(function(v) {return new Fruit(v.name);});
using JSON
var fruits2 = fruits.map(function(v) {return JSON.parse(JSON.stringify(v));});
create and use some cloning method
var fruits3 = fruits.map(function(v) {return cloneObj(v);});
In case 3, a method for cloning could look like:
function cloneObj(obj) {
function clone(o, curr) {
for (var l in o){
if (o[l] instanceof Object) {
curr[l] = cloneObj(o[l]);
} else {
curr[l] = o[l];
}
}
return curr;
}
return obj instanceof Array
? obj.slice().map( function (v) { return cloneObj(v); } )
: obj instanceof Object
? clone(obj, {})
: obj;
}
Using this cloneObj method, Array.map is obsolete.
You can also use var fruitsx = cloneObj(fruits);
The jsFiddle from the link above is modified to demonstrate these methods.
For Array.map, see again MDN
slice can do the trick.
You can also use .map but .slice is normally faster.
var copy = fruits.map(function(item) {return item});
Hope it helps
You can declare a new array and use concat method, so that you concat all values from your array to the new array. Something like this:
var x = ["a","b"];
var a = [];
a = a.concat(x);
console.log(a);
I edited my poor answer.
Best regards.
This should be pretty easy but I'm a little confused here. I want to fill this object:
var obj = { 2:some1, 14:some2, three:some3, XX:some4, five:some5 };
but in the start I have this:
var obj = {};
I´m making a for but I don't know how to add, I was using push(), but is not working. Any help?
You can't .push() into a javascript OBJECT, since it uses custom keys instead of index. The way of doing this is pretty much like this:
var obj = {};
for (var k = 0; k<10; k++) {
obj['customkey'+k] = 'some'+k;
}
This would return:
obj {
customkey0 : 'some0',
customkey1 : 'some1',
customkey2 : 'some2',
...
}
Keep in mind, an array: ['some1','some2'] is basicly like and object:
{
0 : 'some1',
1 : 'some2'
}
Where an object replaces the "index" (0,1,etc) by a STRING key.
Hope this helps.
push() is for use in arrays, but you're creating a object.
You can add properties to an object in a few different ways:
obj.one = some1;
or
obj['one'] = some1;
I would write a simple function like this:
function pushVal(obj, value) {
var index = Object.size(obj);
//index is modified to be a string.
obj[index] = value;
}
Then in your code, when you want to add values to an object you can simply call:
for(var i=0; i<someArray.length; i++) {
pushVal(obj, someArray[i]);
}
For info on the size function I used, see here. Note, it is possible to use the index from the for loop, however, if you wanted to add multiple arrays to this one object, my method prevents conflicting indices.
EDIT
Seeing that you changed your keys in your questions example, in order to create the object, you can use the following:
function pushVal(obj, value, key) {
//index is modified to be a string.
obj[key] = value;
}
or
obj[key] = value;
I'm not sure how you determine your key value, so without that information, I can't write a solution to recreate the object, (as is, they appear random).
_.difference([], [])
this method works fine when i'm having primitive type data like
var a = [1,2,3,4];
var b = [2,5,6];
and the _.difference(a,b) call returns [1,3,4]
but in case i'm using object like
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
doesn't seem to work
try this on for size for finding the difference of an array of objects:
var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];
_.filter(test, function(obj){ return !_.findWhere(test2, obj); });
While the accepted answer is correct, and the other answers give good ideas as well, there is an additional option that's pretty easy to implement with underscore.
This solution relies on each object having a unique ID, but in many cases this will be true, and you can get the difference of two arrays of objects in just two lines of code.
Using underscore's "pluck" method, you can quickly construct an array of all of the ID's in your source set and the target set. From there, all of underscore's array methods will work, difference, union, intersection etc...
After the operation, it is trivial to obtain the list of objects from your source list that you desire. Here's an example:
Verbose:
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });
or, more concisely:
var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });
Of course, this same technique can be extended for use with any of the array methods.
Reason is simply that object with same content are not same objects e.g.
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
a.indexOf({'id':1, 'value':10})
It will not return 0 but -1 because we are searching for a different object
See the source code http://underscorejs.org/underscore.js, _.difference uses _.contains
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
and _.contains ultimately uses indexOf hence will not find objects unless they point to same object.
You can improve the underscore _.contains by looping through all items and calling a compare callback, which you should be able to pass to difference or contains function or you can check this version which improves contains methods
without using underscorejs,
here is the pretty simple method i got solution ...
a = [{'key':'123'},{'key':'222'},{'key':'333'}]
b = [{'key':'123'},{'key':'222'}]
var diff = a.filter(function(item1) {
for (var i in b) {
if (item1.key === b[i].key) { return false; }
};
return true;
});
console.log('result',diff)
I actually can imagine situations where I'd rather use #kontr0l approach than something else, but you have to understand that this approach is quadratic, so basically this code is an abstraction for naïve approach - iterate through all values in two arrays.
There are approaches better than quadratic, I won't use here any big O notation, but here are two main approaches, both are better then naïve one:
iterate through one of the arrays and check for existence in sorted second array using binary search.
put values into set/hash/dictionary/you name it.
As it've been already mentioned, first approach can be adopted for objects if you reimplement standard difference method with using some more flexible analogue of indexOf method.
With second approach we can hit the wall with the fact that, as of Feb'2015, only modern browsers are supporting Sets. As of hashes (well, objects) in javascript, they can have only string-type keys, so any object invoked as key first shoud be converted via toString method. So, we need to provide some => correspondece. On practice in most cases it's pretty straightforward, for instance, for your particular example such correspondence can be just String(obj.id).
Having such correspondence, we also can use following lodas/undercore approach:
var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');
// actually here we can stop in some cases, because
// quite often we need to identify object, but not the object itself -
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);
//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:
var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})
But buy admitting slightly stricter restriction - that we can build correspondence between our set objects and natural numbers we can build even more efficent algorithm, i.e. all our ids are non-negative integers - we can use more efficient algorithm.
The trick is to implement set by introducing two helper arrays this way:
var naturalSet = function (arr) {
var sparse = [];
var dense = [];
var contains = function (i) {
var res = sparse[i] < dense.length && dense[sparse[i]] == i;
return res;
}
var add = function (v) {
if (!contains(v)) {
sparse[v] = dense.length;
dense.push(v);
}
}
arr.forEach(add);
return {
contains: contains,
toArray: function () {
return dense
},
_getDense: function () {
return dense
},
_getSparse: function () {
return sparse
}
}
}
Then we can introduce set with mapping to naturalSet:
var set = function (arr, valueOf) {
var natSet = naturalSet(arr.map(valueOf));
return {
contains: function (item) {
return natSet.contains(valueOf(item))
},
toArray: function () {
var sparse = natSet._getSparse();
var res = natSet._getDense().map(function (i) {
return arr[sparse[i]];
});
return res;
}
}
}
and finally, we can introduce intersection:
var intersection = function(arr1, arr2, valueOf) {
return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}
So, relying on the structure of data you are working can help you sometimes.
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
var array = [];
array = a.map(e => {
if(c.includes(e.id)){
return e;
}
}).filter(r=>r);
Don't get why these answers are so complex unless I'm missing something?
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
// Or use lodash _.differenceBy
const difference = (array1, array2, prop = 'id') =>
array1.filter(item1 =>
!array2.some(item2 =>
item2[prop] === item1[prop],
),
);
// In one array.
console.log(difference(a, b));
// Intersection.
console.log([...difference(a, b), ...difference(b, a)]);
Forgive me for hopping in late here, but this may help:
array_of_objects =
// return the non-matching items (without the expected properties)
_.difference(array_of_objects,
// filter original list for items with expected properties
_.where(
// original list
array_of_objects,
// expected properties
{'id':1, 'value':10}
)
)