JavaScript objects property to array - javascript

I have the following object:
const movies = {
1: {
id: 1,
name: 'Planet Earth',
},
2: {
id: 2,
name: 'Selma',
},
3: {
id: 3,
name: 'Million Dollar Baby',
},
4: {
id: 4,
name: 'Forrest Gump',
},
5: {
id: 5,
name: 'Get Out',
},
};
Then I want an array with only the property id. To do so I've tried something like:
const moviesArray = Object.values(movies);
const idArray = moviesArray.map(movie => Object.values(movie)[0]);
console.log(idArray);
It prints idArray properly but my question is if am I missing a method to solve this problem.

You could use the id property directly:
const
movies = { 1: { id: 1, name: 'Planet Earth' }, 2: { id: 2, name: 'Selma' }, 3: { id: 3, name: 'Million Dollar Baby' }, 4: { id: 4, name: 'Forrest Gump' }, 5: { id: 5, name: 'Get Out' } },
moviesArray = Object.values(movies),
idArray = moviesArray.map(movie => movie.id);
console.log(idArray);

const movies = {
1: {
id: 1,
name: 'Planet Earth',
},
2: {
id: 2,
name: 'Selma',
},
3: {
id: 3,
name: 'Million Dollar Baby',
},
4: {
id: 4,
name: 'Forrest Gump',
},
5: {
id: 5,
name: 'Get Out',
},
};
const moviesArray = Object.values(movies);
const idArray = moviesArray.map(movie => movie.id);
console.log(idArray);

I this case, I'd be more inclined to use movie => movie.id as your mapper function, rather than movie => Object.values(movie)[0].
The issue with your current function is that it assumes id will always happen to be the first property in the Array returned by Object.values. That happens to be true with your current function as written, but I'm not sure you can necessarily guarantee that in the general case. Directly referencing movie.idworks even if the properties come in a different order. It should also be a bit faster, since you don't have to convert eaxh individual object to an Array each time.

I think there wasn't a need for using Object.values in the map part here. It would have been same without it:
const movies = {
1: {
id: 1,
name: 'Planet Earth',
},
2: {
id: 2,
name: 'Selma',
},
3: {
id: 3,
name: 'Million Dollar Baby',
},
4: {
id: 4,
name: 'Forrest Gump',
},
5: {
id: 5,
name: 'Get Out',
},
};
const moviesArray = Object.values(movies);
const idArray = moviesArray.map(movie => movie);
console.log(moviesArray);

May be you can go with more core version.
In my solution the loop will be running only once.
const movies = {
1: {
id: 1,
name: 'Planet Earth',
},
2: {
id: 2,
name: 'Selma',
},
3: {
id: 3,
name: 'Million Dollar Baby',
},
4: {
id: 4,
name: 'Forrest Gump',
},
5: {
id: 5,
name: 'Get Out',
},
};
const idArray = [];
for (let i in movies) {
idArray.push(movies[i].id);
}
console.log(idArray);

Related

Return a subarray with a value changed to reflect parent object

I am trying to change the value of single key in an associative array which is inside another assoc array using javascript.
I have an array like this:
let arr = [{
id: 4,
name: 'test',
docs: [{
id: 1,
name: 'abc'
},{
id: 2,
name: 'xyz'
}]
}, {
id: 8,
name: 'test2',
docs: [{
id: 5,
name: 'abc'
},{
id: 7,
name: 'xyz'
}]
}]
I want to change the value of name of xyz to xyz (test), where test is name key of parent object and get final array as Output:
[{
id: 1,
name: 'abc (test)'
},
{
id: 2,
name: 'xyz (test)'
},
{
id: 5,
name: 'abc (test2)'
},
{
id: 7,
name: 'xyz (test2)'
}]
I am using approach.
let docs = new Array();
arr.forEach((item, index) => {
let docx = item.documents.map(item1 => {
item1.name = item1.name + " ("+item.name+")";
});
docs.push(docx);
});
return docs;
this is returning array of undefined array.
Try a flatMap
let arr = [{ id: 4, name: 'test', docs: [{ id: 1, name: 'abc' },{ id: 2, name: 'xyz' }] }, { id: 8, name: 'test2', docs: [{ id: 5, name: 'abc' },{ id: 7, name: 'xyz' }] }]
const output = arr.flatMap(item =>
item.docs.map(({id,name}) => ({ id, name: `${name} (${item.name})` }))
)
console.log(output)
There is an issue in your data, the docs inner array contains an object with duplicate keys:
let arr = [{
id: 4,
name: 'test',
docs: [{
id: 1,
name: 'abc',
id: 2, // <-- duplicate key
name: 'xyz' // <-- duplicate key
}]
},
If I remove the duplication, you can use this code to create a new object with the name value updated to xyz123 if the original value was xyz:
const original = [{
id: 4,
name: 'test',
docs: [{
id: 1,
name: 'abc'
}, {
id: 2,
name: 'xyz'
}]
}, {
id: 8,
name: 'test2',
docs: [{
id: 1,
name: 'abc'
}, {
id: 2,
name: 'xyz'
}]
}];
const updates = original.map(currentObject => {
const newObject = Object.assign(currentObject);
const newDocs = newObject.docs.map(doc => {
const newDoc = Object.assign(doc);
if (newDoc.name === "xyz") {
newDoc.name = "xyz123";
}
return newDoc;
});
newObject.docs = newDocs;
return newObject
});
console.log(updates);

filter array of object from another array with multiple elements in javascript

I have object and an array:
const response = [
{ id: 1, product: 'EL' },
{ id: 2, product: 'AC' },
{ id: 3, product: 'AC' },
{ id: 4, product: 'AD' },
{ id: 5, product: 'DE' },
];
const elProd = [
"EL"
"DE"
];
i want to filter out the product which has product code in elProd array.
my expectation is below:
const response = [
{ id: 2, product: 'AC' },
{ id: 3, product: 'AC' },
{ id: 4, product: 'AD' },
];
what I tried :
response.filter(obj => {return obj.product == elProd .includes(obj.product)})
const response = [{
id: 1,
product: 'EL'
},
{
id: 2,
product: 'AC'
},
{
id: 3,
product: 'AC'
},
{
id: 4,
product: 'AD'
},
{
id: 5,
product: 'DE'
},
];
const elProd = [
"EL",
"DE"
];
const filteredresp = response.filter((resp) => elProd.indexOf(resp.product) !== -1);
console.log(filteredresp);
response.filter(obj => {return !elProd.includes(obj.product)});
You almost got it, but you have a typo. You need a comma in the elProd array. Here's the code:
const response = [
{ id: 1, product: "EL" },
{ id: 2, product: "AC" },
{ id: 3, product: "AC" },
{ id: 4, product: "AD" },
{ id: 5, product: "DE" },
];
const elProd = [
"EL", // <-- Add the comma.
"DE",
];
// Don't forget to negate the includes here to exclude those you don't want.
const filtered = response.filter((obj) => !elProd.includes(obj.product));
// Result.
console.log(filtered);
You need only the check with includes.
const
response = [{ id: 1, product: 'EL' }, { id: 2, product: 'AC' }, { id: 3, product: 'AC' }, { id: 4, product: 'AD' }, { id: 5, product: 'DE' }],
elProd = ["EL", "DE"],
result = response.filter(obj => !elProd.includes(obj.product));
console.log(result);
You could use filter and some
const response = [
{ id: 1, product: 'EL' },
{ id: 2, product: 'AC' },
{ id: 3, product: 'AC' },
{ id: 4, product: 'AD' },
{ id: 5, product: 'DE' },
];
const elProd = [
"EL",
"DE"
]
res=response.filter(o=>!elProd.some(p=>o.product==p))
console.log(res)

refactoring assistance with reducing some array data objects in JavaScript

I need to reduce data in profiles array in a way such that the final object groups the data in profile obj based on the favorite movie and the users that liked/favorited the movie.
I want something like:
{
'Forrest Gump': ["Nicholas Lain"],
'Planet Earth 1': ["Jane Jones", "Matthew Johnson"]
}
from the following data objects:
const profiles = [
{
id: 1,
userID: '1',
favoriteMovieID: '1',
},
{
id: 2,
userID: '2',
favoriteMovieID: '1',
},
{
id: 3,
userID: '4',
favoriteMovieID: '5',
}
];
const users = {
1: {
id: 1,
name: 'Jane Cruz',
userName: 'coder',
},
2: {
id: 2,
name: 'Matthew Johnson',
userName: 'mpage',
}
};
const movies = {
1: {
id: 1,
name: 'Planet Earth 1',
},
2: {
id: 2,
name: 'Selma',
}
};
I need some ideas in refactoring the following code for it to go back to the users and movies object to grab their names from the IDs I have captured below. Instead of IDs, I need to capture the names.
profiles.reduce(function (acc, obj) {
let key = obj['favoriteMovieID']
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj.userID)
return acc
}, {})
Here is one technique, doing a fold on the profiles, grabbing the movie and person names inside the parameters, and then simply writing a new accumulator with that data. Note that there is a potential performance problem with this, as described in Rich Snapp's excellent article. If that causes you an actual issue, it's easy enough to change this to mutate the accumulator.
I added some additional data to show what happens when the user or the movie isn't in the appropriate lists. If that cannot ever happen, you can simplify the name and person declarations a bit. But I wouldn't recommend it, as things that "can never happen" in fact regularly do happen.
const groupNamesByMovie = (profiles, users, movies) =>
profiles .reduce ((
a, {userID, favoriteMovieID}, _, __,
{name} = movies [favoriteMovieID] || {name: 'Unknown Movie'},
{name: person} = users [userID] || {name: 'Unknown Person'}
) => ({
...a,
[name]: [... (a [name] || []), person]
}), {})
const profiles = [{id: 1, userID: "1", favoriteMovieID: "1"}, {id: 2, userID: "2", favoriteMovieID: "1"}, {id: 3, userID: "4", favoriteMovieID: "5"}, {id: 4, userID: "6", favoriteMovieID: "5"}, {id: 5, userID: "5", favoriteMovieID: "7"}]
const users = {1: {id: 1, name: "Jane Cruz", userName: "coder"}, 2: {id: 2, name: "Matthew Johnson", userName: "mpage"}, 4: {id: 4, name: "Nicholas Lain", userName: "nlain"}, 5: {id: 5, name: "Fred Flintstone", userName: "bedrock1"}}
const movies = {1: {id: 1, name: 'Planet Earth 1'}, 2: {id: 2, name: 'Selma'}, 5: {id: 5, name: 'Forrest Gump'}}
console .log (
groupNamesByMovie (profiles, users, movies)
)
Note that the arguments _ and __ are just meant to be placeholders, since we don't care about reduce's index and array parameters.
Update
There was a request for clarification. For comparison, here's a more imperative version of this same idea:
const getNamesByMovie = (profiles, users, movies) =>
profiles .reduce ((acc, {userID, favoriteMovieID}) => {
const movie = movies [favoriteMovieID]
const name = movie ? movie.name : 'Unknown Movie'
const user = users [userID]
const person = user ? user.name : 'Unknown Person'
const fans = acc [name] || []
return {
... acc,
[name]: [... fans, person]
}
}, {})
And if you wanted to avoid that potential performance problem, you could replace the return statement with something like this:
acc [name] = fans
fans .push (person)
return acc
Either of these does the same sort of thing as the original above. I choose that initial style because I don't like mutating the accumulator object, preferring to always create a new version... and because I prefer working with expressions over statements. But this style does take some getting used to.
You also asked how we passed additional parameters to the reduce callback. We don't. Instead we define some additional parameters and initialize them based on the earlier parameters.
const profiles = [
{
id: 1,
userID: '1',
favoriteMovieID: '1',
},
{
id: 2,
userID: '2',
favoriteMovieID: '1',
},
{
id: 3,
userID: '4',
favoriteMovieID: '5',
}
];
const users = {
1: {
id: 1,
name: 'Jane Cruz',
userName: 'coder',
},
2: {
id: 2,
name: 'Matthew Johnson',
userName: 'mpage',
}
};
const movies = {
1: {
id: 1,
name: 'Planet Earth 1',
},
2: {
id: 2,
name: 'Selma',
}
};
const singleProfileNameFavoriteMovie = profiles.map(profile => `${users[profile.userID].name}'s favorite movie is ${movies[profile.favoriteMovieID].name}` )
const profiles = [
{
id: 1,
userID: '1',
favoriteMovieID: '1',
},
{
id: 2,
userID: '2',
favoriteMovieID: '1',
},
{
id: 3,
userID: '4',
favoriteMovieID: '5',
},
{
id: 4,
userID: '5',
favoriteMovieID: '2',
},
{
id: 5,
userID: '3',
favoriteMovieID: '5',
},
{
id: 6,
userID: '6',
favoriteMovieID: '4',
},
];
const users = {
1: {
id: 1,
name: 'Jane Cruz',
userName: 'coder',
},
2: {
id: 2,
name: 'Matthew Johnson',
userName: 'mpage',
},
3: {
id: 3,
name: 'Autumn Green',
userName: 'user123',
},
4: {
id: 4,
name: 'John Doe',
userName: 'user123',
},
5: {
id: 5,
name: 'Lauren Carlson',
userName: 'user123',
},
6: {
id: 6,
name: 'Nicholas Lain',
userName: 'user123',
},
};
const movies = {
1: {
id: 1,
name: 'Planet Earth 1',
},
2: {
id: 2,
name: 'Selma',
},
3: {
id: 3,
name: 'Million Dollar Baby',
},
4: {
id: 4,
name: 'Forrest Gump',
},
5: {
id: 5,
name: 'Get Out',
},
};
const singleProfileFavoriteMovie = profiles.map(profile => `${users[profile.userID].name}'s favorite movie is ${movies[profile.favoriteMovieID].name}` );
console.log(singleProfileFavoriteMovie);

Removing passed array of objects from an array of objects [duplicate]

I want to filter array of objects by another array of objects.
I have 2 array of objects like this:
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
and I want filter array by anotherArray and return items that is not exist in anotherArray and have sub.
So my desired output is:
[ { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } ]
Note: I've done this with for loop but it work too slow. I want to do this with using Arrays filter method
Code I have with for loop:
for (let i = 0; i < array.length; i += 1) {
let exist = false;
const item = array[i];
for (let j = 0; j < anotherArray.length; j += 1) {
const anotherItem = anotherArray[j];
if (item.id === anotherItem.id) {
exist = true;
}
}
if (item.sub && !exist) {
this.newArray.push({
text: `${item.sub.name} / ${item.name}`,
value: item.id,
});
}
}
Like Felix mentioned, Array#filter won't work faster than native for loop, however if you really want it as functional way, here's one possible solution:
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const r = array.filter((elem) => !anotherArray.find(({ id }) => elem.id === id) && elem.sub);
console.log(r);
You can use Array.filter and then Array.some since the later would return boolean instead of the element like Array.find would:
const a1 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } }, { id: 4, name: 'a4', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const a2 = [ { id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } }, { id: 2, name: 'a2', sub: null }, { id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } }, ];
const result = a1.filter(({id, sub}) => !a2.some(x => x.id == id) && sub)
console.log(result)
You could use JSON.stringify to compare the two objects. It would be better to write a function that compares all properties on the objects recursively.
const array = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 3, name: 'a3', sub: { id: 8, name: 'a3 sub' } },
{ id: 4, name: 'a4', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const anotherArray = [
{ id: 1, name: 'a1', sub: { id: 6, name: 'a1 sub' } },
{ id: 2, name: 'a2', sub: null },
{ id: 5, name: 'a5', sub: { id: 10, name: 'a5 sub' } },
];
const notIn = (array1, array2) => array1.filter(item1 => {
const item1Str = JSON.stringify(item1);
return !array2.find(item2 => item1Str === JSON.stringify(item2))
}
);
console.log(notIn(array, anotherArray));
Ok, let's solve this step by step.
To simplify the process let's suppose that two elements can be considered equals if they both have the same id.
The first approach that I would use is to iterate the first array and, for each element, iterate the second one to check the conditions that you've defined above.
const A = [ /* ... */]
const B = [ /* ... */]
A.filter(el => {
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB && !!B.sub
})
If we are sure that the elements in A and in B are really the same when they have the same ID, we could skip all the A elements without the sub property to perform it up a little bit
A.filter(el => {
if (!el.sub) return false
let existsInB = !!B.find(e => {
return e.id === el.id
}
return existsInB
})
Now, if our arrays are bigger than that, it means that we are wasting a lot of time looking for the element into B.
Usually, in these cases, I transform the array where I look for into a map, like this
var BMap = {}
B.forEach(el => {
BMap[el.id] = el
})
A.filter(el => {
if (!el.sub) return false
return !!BMap[el.id]
})
In this way you "waste" a little bit of time to create your map at the beginning, but then you can find your elements quicker.
From here there could be even more optimizations but I think this is enought for this question
OPTIMIZED VERSION
const array = [{
id: 1,
name: "a1",
sub: {
id: 6,
name: "a1 sub"
}
},
{
id: 2,
name: "a2",
sub: null
},
{
id: 3,
name: "a3",
sub: {
id: 8,
name: "a3 sub"
}
},
{
id: 4,
name: "a4",
sub: null
},
{
id: 5,
name: "a5",
sub: {
id: 10,
name: "a5 sub"
}
},
];
const anotherArray = [{
id: 1,
name: "a1",
sub: {
id: 6,
name: "a1 sub"
}
},
{
id: 2,
name: "a2",
sub: null
},
{
id: 5,
name: "a5",
sub: {
id: 10,
name: "a5 sub"
}
},
];
const dict = anotherArray.reduce((acc, curr) => {
const { id } = curr;
acc[id] = curr;
return acc;
}, {});
const result = array.filter((obj) => {
const search = dict[obj.id];
if (!search && obj.sub) return true;
return false;
});
console.log(result);

How to add object element in array based on condition

I have static array constant of objects something similar to below.
export const EMPLOYEES = [
{
id: 2,
name: ‘John’,
},
{
id: 3,
name: ‘Doe’,
},
{
id: 4,
name: ‘Bull’,
},
{
id: 5,
name: ‘Scott’,
},
];
Now I need to add the last element only based on if some condition is true. Some this like if isAmerican() is true.
Can somebody help me here how to add element based on the condition? Thanks.
You can do it using spread operator:
export const EMPLOYEES = [
{
id: 2,
name: "John",
},
{
id: 3,
name: "Doe",
},
{
id: 4,
name: "Bull",
},
{
id: 5,
name: "Scott",
},
... isAmerican() ? [{ id: 6, name: "Jemmy"}] : []
];
You should never modify (or try to modify) a constant. I can see two ways you can do this:
Create a pure function to return a new constant with the new object added to the array
Use a spread operator in the definition of the constant
Option 1: Pure function
function makeNewArray(array, objectToAppend, isAmerican) {
return isAmerican ? [...array, objectToAppend] : array
}
const EMPLOYEES = [
{
id: 2,
name: "John",
},
{
id: 3,
name: "Doe",
},
{
id: 4,
name: "Bull",
},
{
id: 5,
name: "Scott",
}
];
const arrayWithAmerican = makeNewArray(EMPLOYEES, { id: 6, name: "American Frank"}, true);
const arrayWithoutAmerican = makeNewArray(EMPLOYEES, { id: 6, name: "Not American Frank"}, false);
console.log(arrayWithAmerican);
console.log(arrayWithoutAmerican);
Option 2: Spread operator
function isAmerican(){
// generic code here.
return true;
}
const EMPLOYEES = [
{
id: 2,
name: "John",
},
{
id: 3,
name: "Doe",
},
{
id: 4,
name: "Bull",
},
{
id: 5,
name: "Scott",
},
... isAmerican() ? [{ id: 6, name: "American Frank"}] : []
];
If the condition will be fulfilled, simply push an object to your EMPLOYEES array:
let isAmerican = true;
const EMPLOYEES = [
{
id: 2,
name: "John",
},
{
id: 3,
name: "Doe",
},
{
id: 4,
name: "Bull",
},
{
id: 5,
name: "Scott",
},
];
if(isAmerican) {
EMPLOYEES.push({
id: 6,
name: "Frank"
})
}
console.log(EMPLOYEES)
Fiddle: https://jsfiddle.net/rqx35pLz/

Categories

Resources