lodash: deep copy object but not all properties - javascript

Is there any way to copy an object with lodash, but not all properties.
The only way I know is manually copying it property by property
wanted e.g.:
var obj = {
a: 'name',
b: [1,2,3],
c: {
z: 'surname',
x: []
},
d: {
y: 'surname2',
w: []
}
};
and the result be like
var copy_obj = {
b: [1,2,3],
c: {
z: 'surname',
x: []
}
};
Edit:
I finally opted for:
var blacklist = ['a','d'];
_.cloneDeep(_.omit(obj, blacklist));

The omit serves almost this exact purpose:
_.cloneDeep(_.omit(obj, blacklist));
Fiddle here: https://jsfiddle.net/c639m9L2/

You could use the pick function:
_.pick(obj, 'b', 'c')

You can use the second parameter to JSON.stringify to do this.
JSON.parse(JSON.stringify(obj, ['b', 'c']))

You can use a combination of assign and pick
Object.assign(copy_obj, _.pick(obj, ['b', 'c']));
In this way if copy_obj has other properties you don't override them.

var blacklist = ['a','d'];
_.cloneDeep(_.omit(obj, blacklist));

Related

How to get filtered in and out elements of array at one go in Javascript

I wonder if there is a precise way for getting filtered an unfiltered elements of an array in Javascript, I mean, like, in one go.
Currently, I use a logic like follows:
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = ['a', 'b']
// I want to combine those two expressions somehow
const filteredInResult = myArray.filter(e => filterArray.includes(e))
const filteredOutResult = myArray.filter(e => !filterArray.includes(e))
console.log(filteredInResult)
console.log(filteredOutResult)
I felt like a destructuring-like way might already be there to achieve it, but anyway, I prefer asking you guys if there is a way for getting filtered in & out results in one shot.
EDIT: SO keeps alerting me if this question is similar to the question here, but I used string comparison and includes for brewity above but the filtering expression may be more complex than that. So, the I must underline that the focus of the question is not on difference of two string arrays. I am leaving another example and hope the questions won't be merged :D
// A more complex use case
const myArray = [
{id: 1, value: 'a'},
{id: 2, value: 'b'},
{id: 3, value: 'c'},
{id: 4, value: 'd'},
{id: 5, value: 'e'},
]
const filterArray = ['a', 'b']
// I want to combine those two expressions somehow
const filteredInResult = myArray.filter(e => filterArray.includes(e.value))
const filteredOutResult = myArray.filter(e => !filterArray.includes(e.value))
console.log(filteredInResult)
console.log(filteredOutResult)
If you're worried about iterating twice over the myArray, you might first consider reducing the computational complexity. Because each iteration of the loops calls Array.prototype.includes, and the complexity of Array.prototype.includes is O(n), your code has an overall complexity of O(n ^ 2). (outer loop: O(n) * inner loop: O(n)). So, consider fixing that first: use a Set and Set.has, an O(1) operation, instead of an array and .includes. This is assuming that your actual filterArray is large enough that computational complexity is something to worry about - sets do have a bit of an overhead cost.
As for the other (main) part of the question, one option is to create the two result arrays outside, then push to the appropriate one while iterating:
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = new Set(['a', 'b'])
const filteredInResult = [];
const filteredOutResult = [];
for (const e of myArray) {
(filterArray.has(e) ? filteredInResult : filteredOutResult).push(e);
}
console.log(filteredInResult)
console.log(filteredOutResult)
Could also use reduce, though I don't think it looks very good:
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = new Set(['a', 'b'])
const { filteredInResult, filteredOutResult } = myArray.reduce((a, e) => {
a[filterArray.has(e) ? 'filteredInResult' : 'filteredOutResult'].push(e);
return a;
}, { filteredInResult: [], filteredOutResult: [] });
console.log(filteredInResult)
console.log(filteredOutResult)
You could use .reduce() instead of .filter(), where you use the (numeric) boolean value of includes() as the index for your accumilator like so:
const myArray = ['a', 'b', 'c', 'd', 'e'];
const filterArray = ['a', 'b'];
const [fOut, fIn] = myArray.reduce((a, n) => {
a[+filterArray.includes(n)].push(n);
return a;
}, [[], []]);
console.log(fIn);
console.log(fOut);
I could not destruct but this seems to be simpler to read than the reduce offered elsewhere
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = ['a', 'b']
let filteredOutResult = [];
const filteredInResult = myArray.filter(item => {
if (filterArray.includes(item)) return item;
filteredOutResult.push(item);
});
console.log(filteredInResult,filteredOutResult)
A solution with ramda's partition.
const
array = ['a', 'b', 'c', 'd', 'e'],
filter = ['a', 'b'],
[filteredInResult, filteredOutResult] = R.partition(v => filter.includes(v), array);
console.log(...filteredInResult); // a b
console.log(...filteredOutResult) // c d e
<script src="http://cdn.jsdelivr.net/ramda/latest/ramda.min.js"></script>
One native answer is to use Array#reduce:
const { in, out } = myArr.reduce((acc, v) => {
(filterArray.includes(v) ? acc.in : acc.out).push(v);
return acc;
}, { in: [], out: [] });
And then destructure the returned object

How do I take all of an object's properties and insert them into its own object array property?

For example I want something like:
{
a: 1,
b: 2,
c: 3
}
turned into:
{
d: {
a: 1,
b: 2,
c: 3
}
}
I've tried assigning a new property to that object with the object itself but it shows up as circular so I figure it's a reference instead of the actual properties instead of the actual values. I want to try something like JSON.stringify the object and assign it to the property but I don't know how to turn that string into an object format that I can assign to the property.
let firstObj = {
a: 1,
b: 2,
c: 3
}
let secondObj = {};
secondObj.d = firstObj;
console.log(secondObj);
Basically you create a new object and assign the original object to its property d.
You can use ES6 destructuting to make a shallow copy of the object and put it on a new prop:
let obj = {
a: 1,
b: 2,
c: 3
}
obj.d = {...obj}
console.log(obj)
If that's not an option you can reduce() over the objects keys to make a new object and assign it to d:
let obj = {
a: 1,
b: 2,
c: 3
}
obj.d = Object.keys(obj).reduce((newObj, k) => {
newObj[k] = obj[k]
return newObj
},{})
console.log(obj)
It depends whether you want to make the deep or shallow copy of the object d. (Can the object d have a nested structure?)
The question about efficient ways to clone the object has already been answered here.

Merging or appending array of objects in different object

I am using angularjs where I have a $scope.var1 ={a:10, b:20, c:30}; in which I want to append another value(s) which is infact an array of objects i.e. $scope.myobjects=[{m:10, n:30}, {x:6, y:8}, ....]; after appending this value my $scope.var1 should look like, $scope.var1={a:10, b:20, c:30, m:10, n:30, x:6, y:8};
any idea please.
Thanks
obj ={a:10, b:20, c:30};
arr=[{m:10, n:30}, {x:6, y:8}];
arr.forEach(function(a){
Object.keys(a).forEach(function(key){
obj[key]=a[key];
})
})
console.log(obj);
You could iterate the array and use Object.assign.
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
If not on the user agent available, like on IE, you could use a polyfill.
var target = { a: 10, b: 20, c: 30 };
objects = [{ m: 10, n: 30 }, { x: 6, y: 8 }];
objects.forEach(function (o) {
Object.assign(target, o);
});
console.log(target);
Object.assign.apply(null, [$scope.var1].concat($scope.myobjects))
Please try this one:
var var1 = { a: 10, b: 20, c: 30 };
myobjects = [{ m: 10, n: 30 }, { x: 6, y: 8 }];
myobjects.forEach(function (o) {
for (var p in o) {var1[p] = o[p]};
});
console.log(var1);
Note that this code simply add/update properties based on source object

Lodash reject get return object

var obj = {a: [], b: [1,2], c: [], d: [1]};
How do I get a non-empty array of objects like the following:
{b: [1,2], d: [1]}
You can do what you are after, using pickBy().
var result = _.pickBy(obj, function(val){
return val.length > 0;
});
Fiddle here: https://jsfiddle.net/W4QfJ/3160/
Note: Unlike filter() and reject(), this returns an object, keeping your original structure (rather than an array).
Another way to do this: _.omitBy(obj, _.isEmpty);
_.filter() is what you're looking for:
var obj = {a: [], b: [1,2], c: [], d: [1]};
console.log(_.filter(obj, function(o){ return o.length; }))
If you want to use _.reject() like in your title, you can do something like this:
_.reject({a: [], b: [1,2], c: [], d: [1]},function(o){
return o.length == 0
});
Right now, Lodash has a method called _.omit that does exactly what you need:
> const object = {a: 1, b: 2, c: 3, d: 4}
undefined
> _.omit(object, ['a', 'c'])
{ b: 2, d: 4 }

ES6 specific method to loop through two arrays and find matches in each?

Let's say I have two arrays of objects that I want to compare:
var arr1 = [
{
name: 'A', type: "Dog"
},
{
name: 'B', type: "Zebra"
},
{
name: 'C', type: "Cat"
},
{
name: 'D', type: "Dingo"
}
]
var arr2 = [
{
name: 'A', type: "Wolf"
},
{
name: 'B', type: "Echidna"
},
{
name: 'C', type: "Wallaby"
},
{
name: 'D', type: "Rabbit"
}
]
Pretend that arr1 is old data, and arr2 is updated data coming from an API.
I want to loop through the arrays, finding objects whose name matches. If there is a match, I want to update the type from arr1 to arr2.
I'd do this like so:
for(var i = 0; i<arr1.length; i++){
for(var x = 0; x<arr2.length; x++){
if(arr1[i].name === arr2[x].name){
arr1[i].type = arr2[x].type;
}
}
}
I'm wondering if there are any updated ways in ECMAScript 6 which make this easier to do (in a real world scenario the logic is a lot more complex and looping within a loop feels rather clunky);
In ES2015 you wouldn't use this data structure, you would use maps:
var map1 = new Map([
['A', "Dog"],
['B', "Zebra"],
['C', "Cat"],
['D', "Dingo"]
]);
var map2 = new Map([
['A', "Wolf"],
['B', "Echidna"],
['C', "Wallaby"],
['D', "Rabbit"]
]);
And then, to update map1 with the data from map2, you would use
for(let [key, value] of map2)
map1.set(key, value);
Map operations are required to be sublinear on average. They should be constant if the map is implemented with a hash. Then the total cost would be linear.
Alternatively, since the keys are strings, you can consider using a plain object. You can create it with Object.create(null) to prevent it from inheriting properties from Object.prototype, and assign the properties with Object.assign
var obj1 = Object.assign(Object.create(null), {
A: "Dog",
B: "Zebra",
C: "Cat",
D: "Dingo"
});
var obj2 = Object.assign(Object.create(null), {
A: "Wolf",
B: "Echidna",
C: "Wallaby",
D: "Rabbit"
});
And then, to update obj1 with the data from obj2, you would use
for(let key in obj2)
obj1[key] = obj2[key];
Most probably the object will be implemented using a hash, so each assignment will be constant on average. The total cost would be linear.
You can use a forEach loop (ES5) or the for..of loop from ES6:
for (let item1 of arr1) {
for (let item2 of arr2) {
if(item1.name === item2.name){
item1.type = item2.type;
}
}
}
If these lists are quite long I would suggest putting the updated list into a hash map so your time complexity is linear rather than quadratic.

Categories