array some is not working in nested array - javascript

I want to check if current user is present in nested array or not.
this is few part of my sample array which is getting from API:
[
{
"owner":"abc",
"_id":"xyz77",
"comments":[
],
"likes":[
{
"_id":"9999",
"username":"user1"
},
{
"_id":"9998",
"username":"user2"
}
]
},
{
"owner":"bcd"
}
]
I want to see if user1 is present in likes array or not.
if yes then it should give output like this:
[
{
"owner":"abc",
"user1":true
},
{
"owner":"bcd",
"user1":true
},
{
"owner":"def",
"user1":false
}
]
above result is likes array of owner abc has user1 but not present in owner def.
I tried with array.some for likes array inside forEach of owner array. But not getting proper result.
help is appreciated

You can use a combination of Array.prototype.map and Array.prototype.some to create a resulting array which checks if any of the users in the likes array of each owner object matches your username:
const data = [
{
"owner":"abc",
"_id":"xyz77",
"comments":[],
"likes":[
{
"_id":"9999",
"username":"user1"
},
{
"_id":"9998",
"username":"user2"
}
]
},
{
"owner":"bcd",
"_id":"xyz88",
"comments":[],
"likes":[
{
"_id":"9998",
"username":"user2"
},
{
"_id":"9997",
"username":"user3"
}
]
},
];
const checkUsername = (data, username) => {
return data.map(e => {
const x = { owner: e.owner };
x[username] = e.likes.some(el => el.username === username);
return x;
});
};
console.log(checkUsername(data, 'user1'));
console.log(checkUsername(data, 'user2'));

Its Similar to #awarrier99 answer, Use destructuring along with map and some.
const data = [
{
owner: "abc",
_id: "xyz77",
comments: [],
likes: [
{
_id: "9999",
username: "user1",
},
{
_id: "9998",
username: "user2",
},
],
},
{
owner: "bcd",
},
];
const update = (arr, user) =>
data.map(({ likes = [], owner }) => ({
[user]: likes.some(({ username }) => username === user),
owner,
}));
console.log(update(data, "user1"));

Related

How to check if a value in an array is present in other object and accordingly return a new object

I have an array
const dataCheck = ["Rohit","Ravi"];
I have another array of object
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
I want to check if any value in dataCheck is present in the userData and then return a new array with the below data
const newData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit", status: "present" },
{ name: "Ravi", status: "present" },
];
I tried to do something using loops but not getting the expected results
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" }
];
let newDataValue = {};
let newData = [];
userData.forEach((user) => {
const name = user.name;
dataCheck.forEach((userName) => {
if (name === userName) {
newDataValue = {
name: name,
status: "present"
};
} else {
newDataValue = {
name: name
};
}
newData.push(newDataValue);
});
});
console.log(newData);
My trial gives me repeated results multiple results which is just duplicates
You should use map() and a Set.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const set = new Set(dataCheck);
const output = userData.map(data => set.has(data.name) ? ({...data, status: "present"}): data)
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
A Set allows for lookups in O(1) time and therefore this algorithm works in O(n) time. If you would use the array for lookups (e.g. using indcludes(), find() etc.) the runtime would be O(n²). Although this will certainly not matter at all for such small arrays, it will become more relevant the larger the array gets.
map() is used here because you want a 1:1 mapping of inputs to outputs. The only thing to determine then is, what the output should be. It is either the input, if the value is not in the Set, or it is the input extended by one property status set to "present". You can check for the presence in a Set using the has() method and can use the ternary operator ? to make the decision which case it is.
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
// map through every object and check if name property
// exists in data check with help of filter.
// if it exists the length of filter should be 1 so
// you should return { name: el.name, status: "present" } else
// return { name: el.name }
let newData = userData.map((el) => {
if (dataCheck.filter((name) => name === el.name).length > 0) {
return { name: el.name, status: "present" };
} else {
return { name: el.name };
}
});
console.log("newdata: ", newData);
A better approach would be to use map over userData array, find for matching element in dataCheck, if found return matching element + a status key or just return the found element as it is.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const getUpdatedObject = () => {
return userData.map(userData => {
const userDetail = dataCheck.find(data => userData.name === data);
if(userDetail) return {userDetail, status:"present"}
else return {...userData}
});
}
console.log(getUpdatedObject())
Working fiddle
Loop through userData, check if name is includes in dataCheck. If true add status 'present'.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
for (let user of userData) {
if(dataCheck.includes(user.name)) {
user.status = 'present'
}
}
console.log(userData)
You are seeing repeated results due to the second loop dataCheck.forEach((userName) => { as every loop of dataCheck will fire the if/else statement and add something to the final array. However many values you add to dataCheck will be however many duplicates you get.
Only need to loop through one array and check if the value is in the other array so no duplicates get added.
const dataCheck = ["Rohit", "Ravi"];
const userData = [{ name: "Sagar" }, { name: "Vishal" }, { name: "Rohit" }, { name: "Ravi" }];
let newDataValue = {};
let newData = [];
// loop thru the users
userData.forEach((user) => {
// set the user
const name = user.name;
// check if in array
if (dataCheck.indexOf(name) >= 0) {
newDataValue = {
name: name,
status: "present",
};
}
// not in array
else {
newDataValue = {
name: name,
};
}
newData.push(newDataValue);
});
console.log(newData);
So you will do like this :
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const newUserData = userData.map( user => {
dataCheck.forEach( data => {
if( data === user.name )
user.status = "present";
});
return user;
} );
console.log( newUserData );

Removing repeating elements in a array based on specific data [Discord.js]

I have a problem here that I can't deal with. There is little written about this on the internet. Well, when starting out, I need to make a function that will remove the duplicate data from the table, but the comparison of this data must be based on the data from the table object, below I will give an example because I do not know if I explained it well.
[
{
id: 1234,
user: {
nickname: 'a' <-
}
},
{
id: 1234,
user: {
nickname: 'b' <-
}
},
{
id: 1234,
user: {
nickname: 'a' <-
}
},
]
Data is to be compared according to user.nickname.
I tried to do it this way
array.filter((value, index) => array.indexOf (value.user.nickname) === index)
but all I got was a blank array
[]
If anyone can help, I will be grateful because I have this situation.
Your approach is wrong. Here's one way you can do it instead:
const mapOfNicknames = {};
array.forEach((e)=> {
const nick = e.user.nickname;
// check if nick already exists in map
if ( !mapOfNicknames[nick] ) {
mapOfNicknames[nick] = e;
}
});
// at this point, mapOfNicknames has a unique list
const uniqueArray = Object.keys(mapOfNicknames).map( k => mapOfNicknames[k] );
Using Array.filter as you try, should be a proper aproat:
const users = [
{
id: 1234,
user: {
nickname: "a",
},
},
{
id: 1234,
user: {
nickname: "b",
},
},
{
id: 1234,
user: {
nickname: "a",
},
},
];
let filteruniquebyUserName = users.filter(
(user, index, users) => users.findIndex((compareUser) => compareUser.user.nickname === user.user.nickname) === index
);
console.log(filteruniquebyUserName);
See: How to remove all duplicates from an array of objects?
Another way a little more extended but easier to understand:
const data = [
{
id: 1234,
user: {
nickname: "a",
},
},
{
id: 1234,
user: {
nickname: "b",
},
},
{
id: 1234,
user: {
nickname: "b",
},
},
{
id: 1234,
user: {
nickname: "a",
},
},
];
let elementRepeated = [];
let filteruniquebyUserName = data.filter((user, index, data) => {
if(elementRepeated.includes(user.user.nickname)) return;
const numberOfRepeatUser = data.filter(element => element.user.nickname === user.user.nickname).length;
if(numberOfRepeatUser > 1) elementRepeated.push(user.user.nickname);
return user
});
console.log(filteruniquebyUserName);
Apparently you can't do an indexOf check in a nested object like you are doing it right now. See: Javascript indexOf on an array of objects

Prevent Assigned by Reference

I am attempting to clone a few objects in an array based on some properties.
Given an array of objects:
[
{
id: 1,
data: {
user:
{
user1: 0,
user2: 1,
}
}
},
{
id: 2,
data: {
user:
{
user1: 0,
}
}
},
]
I want to transform the above into:
[
{
id: 1,
data: {
user: 'user1',
user_status: 0
}
},
{
id: 1,
data: {
user: 'user2',
user_status: 1,
}
},
{
id: 2,
data: {
user: 'user1',
user_status: 0,
}
},
]
Every user object within the array should have its user property transformed regardless of how many properties are in the user object. There's other properties in the data object that I want to copy but do not wish to modify.
The closest I got was:
result.rows.forEach((item, index) => {
for ( const user in item.data.user ) {
const notif = Object.assign({}, item);
notif.data.user = user;
notif.data.user_status = item.data.user[user];
result.rows.push(notif);
}
});
However, the above acts as if notif it is assigned by reference(?) and is mutating the original object. Using a console.log during the for in loop results in:
console.log(notif.id, notif.data.user, notif.data.user_status)
// Results in 1, user1, undefined
console.log(item.data.user, item.data.user[user])
// Results in user1, undefined instead of the expect { 'user1': 0 }
This results in an array like:
{
id: 1,
data: {
user: 'user2', // Should be user1
user_status: undefined, // Should be 0
}
},
{
id: 1,
data: {
user: 'user2', // Should be user2 -- hooray but in a bad way
user_status: undefined, // Should be 1
}
}
All of this is running on a Node.js (8.11.1) server.
The data in your item references the original data object, because Object.assign gives a shallow clone, not a deep clone.
This would probably be achieved most elegantly by reduce-ing into an array in one go, extracting all the primitives immediately, rather than trying to work with the (by-reference) objects:
const input=[{id:1,data:{user:{user1:0,user2:1,}}},{id:2,data:{user:{user1:0,}}},]
const output = input.reduce((a, { id, data: { user: users }}) => {
Object.entries(users).forEach(([user, user_status]) => {
a.push({ id, data: { user, user_status }});
});
return a;
}, []);
console.log(output);
A fix to your original code would involve cloning the data property as well:
const input=[{id:1,data:{user:{user1:0,user2:1,}}},{id:2,data:{user:{user1:0,}}},]
const output = [];
input.forEach((item, index) => {
for (const user in item.data.user) {
const notif = Object.assign({}, item);
notif.data = Object.assign({}, item.data);
notif.data.user = user;
notif.data.user_status = item.data.user[user];
output.push(notif);
}
});
console.log(output);
You can achieve this using two .forEach loops. You can't loop with a .forEach inside an object, instead, we will loop over Object.keys of the object. Which is essentially an array of keys.
Then for each fragment, for example
{
id: 1,
data: {
user: 'user1',
user_status: 0
}
}
we can push a brand new object to res.
Here is the code:
let res = [];
data.forEach((e, i, arr) => Object.keys(e.data.user).forEach((k, j) => {
res.push({
id: e.id,
data: {
user: k,
user_status: j
}
});
}));
console.log(res);
<script>
const data=[{id:1,data:{user:{user1:0,user2:1,}}},{id:2,data:{user:{user1:0,}}}];
</script>
One way would be to transform each object in the array using map into a subarray of new objects, then flatten the result using reduce.
const input = [
{
id: 1,
data: {
user:
{
user1: 0,
user2: 1,
}
}
},
{
id: 2,
data: {
user:
{
user1: 0,
}
}
},
];
const output = input.map(obj => Object.keys(obj.data.user)
.map(user => ({
id: obj.id,
data: {
user, user_status: obj.data.user[user]
}
})
))
.reduce((a, b) => a.concat(b));
console.log(output)

Joi - validate property against value in array of objects

Im trying to validate a value of a particular property in a payload against a defined array of objects.
for example
payload
{
a:[1, 2]
}
the value of "a" must be a one of the id defined in a array of objects (multiple values allowed)
[
{
"id":1,
"share":{
"x":100,
"y":0,
"z":0
}
},
{
"id":2,
"share":{
"x":90,
"y":0,
"z":10
}
}
....and so on
]
Could you please help advising if this can be achieved with Joi?
Thanks, Gowrish
Joi's array.validate() with items should do what you're looking for.
const Joi = require("joi")
const input = { a: [ 1, 2 ] }
const objects = [{ id: 1 }, { id: 2 }, { id: 3 }]
const schema = {
a: Joi.array().items(Joi.any().valid(objects.map(o => o.id)))
}
const result = Joi.validate(input, schema, (err, result) => {
if (err) {
console.error('error:', err)
return
}
console.log('result:', result)
})

How to get parent property in JS object?

Not sure if title is formulated correct, but I have a JS object that looks like:
parent:{
children:[
{
id: "1"
user:[
{
id: 'aa',
email:'aa#google.com'
},
{
id: 'b',
email:'bbb#google.com'
},
]
},
{
id:"2",
user: [
{
id:'aa',
email:'aa#google.com'
},
{
id:'eee',
email:'eee#google.com'
}
]
}
]
}
The object is way bigger but follows the above structure.
How would I go to get a list of topics each user is on, filtered b ID?
E.g. 'aa' participates in children 1 and 2
'b' participates in child 1
etc.
I figure it out I have to map the object but not sure how to proceed after that
Assuming, you want an object with participant as key and all topic id in an object, then you could iterate the arrays an build a property with the associated id.
var data = { project: { topics: [{ id: "1", participants: [{ id: 'aa', email: 'aa#google.com' }, { id: 'b', email: 'bbb#google.com' }, ] }, { id: "2", participants: [{ id: 'aa', email: 'aa#google.com' }, { id: 'eee', email: 'eee#google.com' }] }] } },
result = Object.create(null);
data.project.topics.forEach(function (a) {
a.participants.forEach(function (b) {
result[b.id] = result[b.id] || [];
result[b.id].push(a.id);
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can write a function like this one:
function findTopicByID(id) {
let findedTopic = {};
let topics = obj.project.topics;
topics.map((topic) => {
if(parseInt(topic.id) === id) findedTopic = topic;
});
return findedTopic;
}
This function return the finded topic with the corresponding id or an empty object.
You can loop the topic array and build a new resulting array of users, if a user already exist then just update the users topic list, else create a new user with name, email, and a topic list.
let result = [];
project.topics.forEach((t) => {
t.participants.forEach((p) => {
let found = result.find((r) => r.name === p.id);
if (found) {
found.topics.push(t.id);
} else {
result.push({
name: p.id,
email: p.email,
topics: [t.id]
});
}
});
});
so now when you have the resulting array, you can just find a user and get which topics she participates in
let aa = result.find((r) => r.name === 'aa');
console.log(aa.topics);

Categories