I was getting this weird result when I'm trying to construct an array of objects by using for...in loop for a object
const obj = {
name: 'John',
age: '25'
}
for (const property in obj) {
const obj1 = {
prop1: property,
prop2: obj[property]
}
const result = [].push(obj)
console.log(result)
}
I was expecting the result to be
[{prop1: 'name', prop2: 'John'}, {prop1: 'age', prop2: '25'}]
Could anyone please help?
push returns the new length of the array, which is why you see 1. Move the array initialization out of the loop and log after the loop is finished:
const obj = {
name: 'John',
age: '25'
}
const result = []
for (const property in obj) {
const obj1 = {
prop1: property,
prop2: obj[property]
}
result.push(obj1)
}
console.log(result)
You are pushing to a new Array that is scoped inside of your loop. You really want to do:
const obj = {
name: 'John',
age: '25'
}
const results = [];
for(let p in obj){
results.push({
prop1: p,
prop2: obj[p]
});
}
console.log(results)
Related
I am programming a function that will handle javascript array filtering. I know the values by which I want to filter so I know how to do it in a fairly easy way, but I would like the code to be more extensible.
I wrote such a function:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter((item) => {
if (filters.prop3 && filters.prop2) {
return item.prop3 === filters.prop3 && item.prop2 === filters.prop2;
}
if (filters.prop3) {
return item.prop3 === filters.prop3;
}
if (filters.prop2) {
return item.prop2 === filters.prop2;
}
});
}
I am not completely satisfied with it. I think it could be written better. If the 3rd argument comes, I don't want to add it to the if - it should be automatic.
I've searched several topics on stackoverflow, looked through the lodash documentation looking for some good solution but I have no idea what I can do better with this.
If I understood correctly, you want to only keep the items that match all the filter:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = {prop2: 'Paris', prop3: '987'};
const handleFilters = (items, filters) => {
return items.filter((item) =>
Object.entries(filters).every(([key, val]) => item[key] === val)
)
};
console.log(handleFilters(items, filters))
This basically checks that every (key, val) of the filter exists in the item
private handleFilters (items, ...props) {
return items.filter(item => props.every(prop => item[prop] === prop));
}
To use the filter object dynamically, you could look into using something like Object.keys().
Here's your example code changed to use that:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter(item => {
return Object.keys(filters).some(filterKey => item[filterKey] === filters[filterKey])
});
}
It loops through the items, the same as you already were doing. However, now it also loops through the keys set in the filters object. The some() function returns a boolean; true if the callback is true and false if the callback is false. Here it checks if the value in item[filterKey] is the same as the value in filters[filterKey].
It's important to note that this returns the item if any of the filter values matches with an item's property. If all values must be in the filtered item, you'll want to use Object.keys(filters).every(filterKey => item[filterKey] === filters[filterKey]).
The every function returns true only if all filterKeys have the same value as the keys checked in item.
It is also possible to use Object.entries(), which allows directly using the value, but for this example I chose to use just the keys, for consistency.
You can filter by using Object.key , filter and every method in precise manner.
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilter = (items, filters) => {
return items.filter((item) => Object.keys(filters).every((key) => item[key] === filters[key]));
}
console.log(handleFilter(items, filters))
You can remove your multiple if blocks by using for..in
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter((item) => {
for(const key in filters) {
if (filters[key] !== item[key]) {
return false;
}
}
return true;
});
}
console.log(handleFilters(items, filters))
It does the same as your code was doing.
Some Improvement over #David Alvarez approach, here i have used Object.keys instead of Object.entries as Object.key is faster than Object.entries here is the comparison: https://www.measurethat.net/Benchmarks/Show/3685/0/objectentries-vs-objectkeys-vs-objectkeys-with-extra-ar
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = {prop2: 'Paris', prop3: '987'};
const handleFilters = (items, filters) => (
items.filter((item) =>
Object.keys(filters).every(key => item[key] === filters[key])
)
);
console.log(handleFilters(items, filters))
Well, basically I have an array that has an objects with names and this objects has an array of objects inside, looks likes this:
var array = [
{articles: [
{number: "123"},
{number: "143"},
]},
{paragraph: [
{number: "197"},
]},
]
And I'm really willing to get a object value in return, like this
{articles: [...], paragraph: [...]}
Can someone help me, please?
This can be done using an array reducer.
const array = [
{ articles: [{ number: "123" }, { number: "143" }] },
{ paragraph: [{ number: "197" }] },
];
const formatted = array.reduce((accumulator, currentValue) => {
const [[key, value]] = Object.entries(currentValue);
return { ...accumulator, [key]: value }
}, {});
console.log(formatted);
What we're doing is initializing our reducer with an empty object on line 9, and iterating over the array. Each time we iterate, we're returning the object with the new key and value appended to the end of it.
You could group by the outer properties of the nested objects.
const
array = [{ articles: [{ number: "123" }, { number: "143" }] }, { paragraph: [{ number: "197" }] }],
result = array.reduce((r, o) => {
Object
.entries(o)
.forEach(([k, a]) => (r[k] ??= []).push(...a));
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Iterate over the array, and use Object.entries to get the keys and values.
const array=[{articles:[{number:"123"},{number:"143"}]},{paragraph:[{number:"197"}]}];
const out = {};
for (const obj of array) {
const [[key, value]] = Object.entries(obj);
out[key] = value;
}
console.log(out);
let array = [
{articles: [
{number: "123"},
{number: "143"},
]},
{paragraph: [
{number: "197"},
]},
]
let obj = {}
array.forEach(a => {
if('articles' in a){
obj.articles = a.articles
}
if('paragraph' in a){
obj.paragraph = a.paragraph
}
})
console.log(obj)
I'm looking for way to remove empty or null props, on my example obj2, I want to avoid copying the birthPlace property or any other prop that comes empty.
const obj1 = { firstName: 'Foo', age: 22 };
const obj2 = { lastName: 'Bar', gender: 'M', birthPlace: '' };
const newObj = { ...obj1, ...obj2 };
Desired result:
{firstName: 'Foo', age: 22, lastName: 'Bar', gender: 'M'}
Is it possible using Conditional Objects props using Spread Operators in javascript?
const updateUserObj = {
...(obj1 !== check here<hasPropEmpty> && obj2)
}
There's no shorthand for it, but you can easily write a function that filters out those properties.
function nonEmptyProps(obj) {
return Object.fromEntries(Object.entries(obj).filter(([k, v]) => v !== null && v !== ''));
}
const obj1 = { firstName: 'Foo', age: 22 };
const obj2 = { lastName: 'Bar', gender: 'M', birthPlace: '' };
const newObj = {...nonEmptyProps(obj1), ...nonEmptyProps(obj2)};
console.log(newObj);
Using Object#entries you get the key-value pairs of an object, then, using Array#filter you iterate over these pairs to filter out the ones with empty values. Then, using Object#fromEntries you construct back the resulting pairs to an object.
const filterProps = (obj = {}) =>
Object.fromEntries(
Object.entries(obj).filter(([key, value]) =>
value !== null && value !== undefined && value !== ''
)
);
const obj1 = { firstName: 'Foo', age: 22 };
const obj2 = { lastName: 'Bar', gender: 'M', birthPlace: '' };
const newObj = { ...filterProps(obj1), ...filterProps(obj2) };
console.log(newObj);
For example, I got an object like this:
obj1 = {
name: 'Bob',
age: 20,
career: 'teacher'
}
Now I need to duplicate part of its properties instead all of them.
obj2 = {
name: '',
age: '',
}
I know I can do it like obj2.name = obj1.name, which will be verbose if many properties need to be duplicated. Are there any other quick ways to solve this problem?
I tried
let {name: obj2.name, age: obj2.age} = obj1;
but got error.
Actually you don't need object destructuring, just simple assignment:
obj2 = { name: obj1.name, age: obj1.age }
Now, obj2 holds wanted properties:
console.log(obj2);
// Prints {name: "Bob", age: 20}
If you want to merge old properties of obj2 with new ones, you could do:
obj2 = { ...obj2, name: obj1.name, age: obj1.age }
Drop the let (you're not declaring variables) and surround with parentheses:
({name: obj2.name, age: obj2.age} = obj1);
I guess you can use ES6 Object destructuring syntax
var obj = { name: 'kailash', age: 25, des: 'backenddev'}
({name} = obj);
You could use the target object as template for the properties and assign the values of obj1 with a default value of the target object.
var obj1 = { name: 'Bob', age: 20, career: 'teacher' },
obj2 = { name: '', age: '' };
Object.keys(obj2).forEach(k => obj2[k] = obj1[k] || obj2[k]);
console.log(obj2);
Another solution would be to write a reuable method for this. You can supply an object and the methods you would like to copy.
const duplicatePropertiesFromObject = (obj, propertyNames = [], newObj = {}) => {
Object.keys(obj).forEach((key) => {
if (propertyNames.includes(key)) {
newObj[key] = obj[key];
}
});
return newObj;
}
I have an array of objects, and need to see if a key exists in any of them. Here is what I am doing now:
const arr = [{ id: 1, foo: 'bar' }, { id: 2 }]
arr.map(o => o.foo && true).includes(true)
// true
Is there any better/more accepted way to do this?
You can use Array#some
var arr = [{ id: 1, foo: 'bar' }, { id: 2 }]
result = arr.some(o => 'foo' in o)
console.log(result)
Difference between every() and some()
every:
It will check for the existence of the given key on all object, and return false if not all of them have this key.
some:
It will check if at least one object has that key and if there is it already returns true.
const arr = [{ id: 1, foo: 'bar' }, { id: 2 }]
var result = arr.some((value, index) => {
return value.hasOwnProperty('bar')
});
console.log(result);
I'd use the Array.prototype.some() function:
const arr = [
{ id: 1, foo: 'bar' },
{ id: 2 }
];
var result = arr.some(e => e.hasOwnProperty('foo'));
console.log("The array contains an object with a 'foo' property: " + result);
var result = arr.some(e => e.hasOwnProperty('baz'));
console.log("The array contains an object with a 'baz' property: " + result);
If you just want a true/false to determine if the element is in there, use 'some'. It returns true/false.
const arr = [{ id: 1, foo: 'bar' }, { id: 2 }];
var key = 'foo';
var isInArray= arr.some(function(val, i) {
return val[i][key];
});
You could use Array#some
var arr = [{ id: 1, foo: 'bar' }, { id: 2 }],
result = arr.some(o => 'foo' in o);
console.log(result);