In Javascript remove keys from object not in an Array - javascript

Suppose I have a list of objects with many keys and I want to keep only certain keys from them.
This is how I am doing it.
The Problem with other good solutions on SO are that if a key is not present in the keys to keep it still adds a key, value where the value is undefined.
let data = [{
'a': 1,
'b': 2,
'c': 3
},
{
'a': 1,
'c': 3,
'd': 4
}]
const keys_to_keep = ['a', 'b']
data = data.map((obj) => {
Object.keys(obj).forEach(function(key) {
if(!keys_to_keep.includes(key))
delete obj[key]
});
return obj;
})
Output :
[ { a: 1, b: 2 }, { a: 1} ]
Is there a better way to get this done.
Any help is appreciated.

A couple of improvements.
You're using .map() which creates a new array, but then you're just assigning it to the old variable. So, you apparently don't need to create a new array at all, you can just iterate the one you have and modify it.
Put the properties you want to keep in a Set instead of an Array for potentially faster lookup.
for/of loops are generally favored over .forEach() loops because of better flow control options (break, continue, return, etc...) and more opportunities for compiler optimization.
let kv = [{
'a': 1,
'b': 2,
'c': 3
},
{
'a': 1,
'b': 2,
'c': 3,
'd': 4
}]
const l = new Set(['a', 'b']);
for (let obj of kv) {
for (let prop of Object.keys(obj)) {
if (!l.has(prop)) {
delete obj[prop];
}
}
}
console.log(kv);

You can use Object.fromEntries, after map and filter to keep only the relevant keys:
let data = [{'a': 1,'b': 2,'c': 3},{'a': 1,'c': 3,'d': 4}]
const keys_to_keep = ['a', 'b']
var result = data.map(obj =>
Object.fromEntries(keys_to_keep.map(key =>
obj.hasOwnProperty(key) && [key, obj[key]]
).filter(Boolean))
);
console.log(result);

Related

javascript equivalent to [x for x in array]

Is there any operation in Javascript just like [x for x in array] in python?
For example, I'm using javascript to reading a json file where there're dozens of (key, value) pairs needed to be handled(or transformed into other format). And I thought working in this way is stupid:
let transformed = []
for (let key in json){
transformed = [ /* doing some transform*/ ]
}
Is there anything like:
let transformed = [
lambda function1(key), lambda function2(value) for key, value in json
]
Thanks in advance.
The rough equivalent of Python's list comprehension is Array.map:
const myArray = [1, 2, 3]
const transformed = myArray.map((item) => item + 1)
// [2, 3, 4]
But your example is not about an array, but about an Object with keys and values. In Python, this would be a dict, and you'd use a dict comprehension along the lines of {function1(key): function2(value) for key, value in my_dict.items()}.
In JavaScript, you can turn such an object into an array with Object.entries, then perform the map, and finally transform it back into an object using Object.fromEntries:
const myObject = { a: 1, b: 2 }
const transformed = Object.fromEntries(Object.entries(myObject)
.map(([key, value]) => [key + 'x', value + 1]))
// { ax: 2, bx: 3 }
Note that fromEntries is fairly new and you might need to add a polyfill for it.
You can use a code likes this. You must use a function that handle operation on current single item.
const words = ['hello', 'bird', 'table', 'football', 'pipe', 'code'];
const capWords = words.forEach(capitalize);
function capitalize(word, index, arr) {
arr[index] = word[0].toUpperCase() + word.substring(1);
}
console.log(words);
// Expected output:
// ["Hello", "Bird", "Table", "Football", "Pipe", "Code"]
First of all, javascript does NOT support Associative Arrays. If you are used to them in Python, PHP, and other languages you need to do a little workaround in JS to achieve the same functionality.
The most common way to simulate an associative array is using an object.
let testObject = {name: "Color", value: "Red"};
And then you push every object into an array so you end up with something like this:
let testArray = [{name: "Color", value: "Red"}, {name: "Color", value: "Blue"}];
Once you have this array consisting of objects, you can use map function to go through every object in the array and do whatever you want with it.
testArray.map((item, index) => {
console.log("The value of "+index+". item is: "item.value);
})
You can use Array.map() function. It work pretty like Array.forEach() function
const numbers = [1, 2, 3, 4, 5]
let newArray = numbers.map((element) => {
return element * 2
})
console.log(newArray) // excepted : [ 2, 4, 6, 8, 10 ]
It can be reduce using
const numbers = [1, 2, 3, 4, 5]
let newArray = numbers.map(element => element * 2)
console.log(newArray) // excepted : [ 2, 4, 6, 8, 10 ]
For more informations, you can this documentation https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/map

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.

Removing Duplicates in Arrays

I am trying to pass a function that removes duplicates from an array. It should handle strings, object, integers as well. In my code so far I am showing that it will handle strings but nothing else. How can Imake this function universalto handle numbers,handle arrays,handle objects, and mixed types?
let unique = (a) => a.filter((el, i ,self) => self.indexOf(el) ===i);
In this function I hav unique() filtering to make a new array which checks the element and index in the array to check if duplicate. Any help would be appreciated.
i think the first you should do is to sort the array ( input to the function ). Sorting it makes all the array element to be ordered properly. for example if you have in an array [ 1, 3, 4, 'a', 'c', 'a'], sorting this will result to [ 1 , 3 , 4, 'a', 'a' , 'c' ], the next thing is to filter the returned array.
const unique = a => {
if ( ! Array.isArray(a) )
throw new Error(`${a} is not an array`);
let val = a.sort().filter( (value, idx, array) =>
array[++idx] != value
)
return val;
}
let array = [ 1 , 5, 3, 2, "d", "q", "b" , "d" ];
unique(array); // [1, 2, 3, 5, "b", "d", "q"]
let obj = { foo: "bar" };
let arraySize = array.length;
array[arraySize] = obj;
array[arraySize++] = "foo";
array[arraySize++] = "baz";
array[arraySize++] = obj;
unique(array); // [1, 2, 3, 5, {…}, "b", "baz", "d", "foo", "hi", "q"]
it also works for all types, but if you pass in an array literal with arrays or objects as one of its element this code will fail
unique( [ "a", 1 , 3 , "a", 3 , 3, { foo: "baz" }, { foo: "baz" } ] ); // it will not remove the duplicate of { foo: "baz" } , because they both have a different memory address
and you should also note that this code does not return the array in the same order it was passed in , this is as a result of the sort array method
Try using sets without generics. You can write a function as
Set returnUnique(Object array[]) {
Set set=new HashSet();
for (Object obj:array) {
set.add(obj);
}
return set;
}

Why _.pick(object, _.identity) in lodash returns empty Object?

I'm trying to move underscore to lodash. But this line of code baffles me.
On my current project we have this line of code.
obj = _.pick(obj, _.identity);
Which is pretty obvious that it's trying to delete empty property.
Now when I switch to lodash, the same line of code returns empty object for me.
I'm trying to figure why. How do I achieve the same effect in lodash?
I tried this on both lodash and underscore websites. They produce different results.
This is from lodash
var obj = {_v:'10.1', uIP:'10.0.0.0', _ts:'123'}
_.pick(obj, _.identity);
Object {}
This is from underscore
var obj = {_v:'10.1', uIP:'10.0.0.0', _ts:'123'}
_.pick(obj, _.identity);
Object {_v: "10.1", uIP: "10.0.0.0", _ts: "123"}
Why _.pick(object, _.identity) in lodash returns empty Object?
Because pick in lodash expects an array of property names to be passed to it:
var object = { 'a': 1, 'b': '2', 'c': 3 };
_.pick(object, ['a', 'c']);
// → { 'a': 1, 'c': 3 }
How do I achieve the same effect in lodash?
Lodash has a method called pickBy which accepts a callback function:
var object = { 'a': 1, 'b': '2', 'c': 3 };
_.pickBy(object, _.isNumber);
// → { 'a': 1, 'c': 3 }
I had the same issue, lodash has a slightly different name for this method than underscore:
var object = { 'a': 1, 'b': '2', 'c': 3 };
_.pickBy(object, _.isNumber);
// → { 'a': 1, 'c': 3 }

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