Get value from child Array with a given ID - javascript

I have the following Array and I'm trying to print the child array value using .find by providing an ID
connections = [
{
group: 'a',
items: [
{
id: '1',
name: 'Andre'
},
{
id: '2',
name: 'David'
}
]
},
{
group: 'b',
items: [
{
id: '3',
name: 'Brandon'
}
]
},
]
I have tried the following in my Angular app,
getUser(id) {
this.activeItem = this.connections.items.find(data => data.id === id);
console.log(this.activeItem);
}
I'm providing the correct ID but I'm getting an error saying,
error TS2339: Property 'items' does not exist on type....
Thank you.

You can use filter and some methods. This approach will filter array and your array will contain only desired items:
let connections = [
{
group: 'a',
items: [
{
id: '1', name: 'Andre'
},
{
id: '2', name: 'David'
}
]
},
{
group: 'b',
items: [
{
id: '3', name: 'Brandon'
}
]
},
]
let id = 3;
// ONE WAY
const result = connections.filter(f=> f.items.some(s=> s.id == id))
.flatMap(fm => fm.items);
console.log(`result: `, result);
// OR ANOTHER WAY:
const resultWithGroup = connections.filter(f=> f.items.some(s=> s.id == id));
const resultItem = Object.assign({}, ...resultWithGroup).items.find(f => f.id == id);
console.log(`resultItem: `, resultItem);
console.log(`resultItem as an array: `, [resultItem]);
In addition, it is possible to use flatMap method. By using this approach you are getting all items with desired id and then find the first element with id == 3:
let connections = [
{
group: 'a',
items: [
{
id: '1', name: 'Andre'
},
{
id: '2', name: 'David'
}
]
},
{
group: 'b',
items: [
{
id: '3', name: 'Brandon'
}
]
},
]
const result = connections.flatMap(f => f.items).find(f => f.id == id);
console.log(`result as array`, [result]);

You can use flatMap
Try like this:
getUser(id) {
this.activeItem = this.connections.flatMap(x => x.items).find(data => data.id === id);
console.log(this.activeItem);
}
Working Demo

As i can see from the Json object, the items arrays are grouped inside their parent objects. So first you would have to flatten the grouped array:
let items = []
connections.forEach(obj => obj.items.forEach( item => items.push(item)))
Now the items array would only be item objects so it will be easier to do a find:
items.find(item => item.id == 3)

You are trying to use an array without specifying the element of the array
-----------------\/ here
this.connections[0].items.find(data => data.id === id);

The reason your "this.connections.items.find" is not working is that connections variable here represents an array of objects, you cannot directly access a key that is inside an objects contained in an array of objects.
Use this code instead:
this.activeItem = this.connections.filter(obj => obj.items.find(val => val.id == id));
console.log(this.activeItem.items);

connections variable is an array and you are trying to access it as an object. PFB the below code it should be working fine for you.
getUser(id) {
this.activeItem = connections.find(function(element) { return element.items.find(function(el){return el.id==id;}); });
console.log(this.activeItem);
}

Try this. (You made mistake with connections.items)
getUser(id) {
let activeItemIndex = -1;
this.connections.forEach((c) => {
activeItemIndex = c.items.findIndex(item => item.id === id);
if (activeItemIndex > -1) {
this.activeItem = c.items[activeItemIndex];
}
});
}

It is normal that you get this error: "error TS2339: Property 'items' does not exist on type....".
Actually, 'connections' is an array and does not have any prperty 'items'.
'items' is an attribut of the elements contained in 'connections' array.
You can try something like:
getUser(id) {
for (const element of this.connections) {
this.activeItem = element.items.find(data => data.id === id);
if (this.activeItem) {
break;
}
}
console.log(this.activeItem);
}
Once 'this.activeItem' is found we exit the loop with the 'break;' statement.

You have to specify index of connections.
But this is the better solution, Because you have all the users in one place:
getUser(id) {
users = connections.reduce((users, item)=>{
users.push(...item.items);
return users;
}, []);
this.activeItem = users.find(data => data.id === id);
console.log(this.activeItem);
}

Related

Find missing id between an array of objects and an array of id

I have an array of objects:
const users = [
{
name:John,
id:1
},
{
name:Dude,
id:2
}
]
And an array of ids:
const ID = [1,2,5]
I would like to to know that 5 is the missing ID so I can fetch that ID.
I have tried nesting for loops
users.forEach((user, index) => {
let userExists = false;
ID.forEach((id) => {
if (user.id == id) {
userExists = true;
return;
}
});
if (!userExists) {
console.log('user doesnt exists');
}
});
There might be a simple solution but currently I can't wrap my head around it
Thanks!
Its very easy to do that, use filter()
const users = [
{
name:"John",
id:1
},
{
name:"Dude",
id:2
}
]
const ID = [1,2,5]
console.log(ID.filter(x=> !users.find(f=> f.id == x)));
Modifying ID in place, using Array#forEach to iterate through each user and Array#splice to remove an item from ID.
const users = [ { name: 'John', id: 1 }, { name: 'Dude', id: 2 }, ];
const ID = [1, 2, 5];
users.forEach(({ id }) => ID.splice(ID.indexOf(id), 1));
console.log(ID);

How to access an array of objects which is a key-value pair

i want to access the id 'qwsa221' without using array index but am only able to reach and output all of the array elements not a specific element.
i have tried using filter but couldnt figure out how to use it properly.
let lists = {
def453ed: [
{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
Use Object.keys() to get all the keys of the object and check the values in the array elements using . notation
let lists = {
def453ed: [{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
Object.keys(lists).forEach(function(e) {
lists[e].forEach(function(x) {
if (x.id == 'qwsa221')
console.log(x)
})
})
You can use Object.Keys method to iterate through all of the keys present.
You can also use filter, if there are multiple existence of id qwsa221
let lists = {
def453ed: [
{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
let l = Object.keys(lists)
.map(d => lists[d]
.find(el => el.id === "qwsa221"))
console.log(l)
you can do it like this, using find
let lists = {
def453ed: [
{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
console.log(
lists.def453ed // first get the array
.find( // find return the first entry where the callback returns true
el => el.id === "qwsa221"
)
)
here's a corrected version of your filter :
let lists = {def453ed: [{id: "qwsa221",name: "Mind"},{id: "jwkh245",name: "Space"}]};
// what you've done
const badResult = lists.def453ed.filter(id => id === "qwsa221");
/*
here id is the whole object
{
id: "qwsa221",
name: "Mind"
}
*/
console.log(badResult)
// the correct way
const goodResult = lists.def453ed.filter(el => el.id === "qwsa221");
console.log(goodResult)
// filter returns an array so you need to actually get the first entry
console.log(goodResult[0])

How to remove child by id and retrive the parent objects using filter

I am trying to filter the parent, by removing it's child id only by not matching. in case if there is no child exist, the parent should be removed.
I try like this, but not works.
var rm = 7;
var objects = [
{
name: "parent1",
id: 1,
blog: [
{
name: "child1",
id: 1
},
{
name: "child2",
id: 2
}
]
},
{
name: "parent2",
id: 2,
blog: [
{
name: "child3",
id: 3
},
{
name: "child4",
id: 4
}
]
},
{
name: "parent3",
id: 3,
blog: [
{
name: "child5",
id: 5
},
{
name: "child6",
id: 6
}
]
},
{
name: "parent4",
id: 3,
blog: [
{
name: "child6",
id: 7
}
]
},
]
var result = objects.filter(value => {
if(!value.blog) return;
return value.blog.some(blog => blog.id !== rm)
})
console.log(result);
What is wrong here, or some one show me the correct approach?
looking for :
need to remove the blog if the id is same as rm, parent with other children required to exist.
need to remove the parent, after remove the children, in case there is no child(blog) exist.
Live Demo
Loop through the list of parents, and inside that loop, try to remove blogs with the given id first. Once you have done that, you can check if the blogs property has became empty, and if so, filter it out:
// We're going to filter out objects with no blogs
var result = objects.filter(value => {
// First filter blogs that match the given id
value.blog = value.blog.filter(blog => blog.id !== rm);
// Then, if the new length is different than 0, keep the parent
return value.blog.length;
})
I think the below code is what you are looking for
var result = objects.map(value => {
const blog = value.blog.filter(blog => blog.id !== rm);
if(blog.length === 0) {
return;
}
value.blog = blog;
return value;
}).filter(item => item);
Demo: https://jsfiddle.net/7Lp82z4k/3/
var result = objects.map(parent => {
parent.blog = parent.blog.filter(child => child.id !== rm);
return parent}).filter(parent => parent.blog && parent.blog.length > 0);

not using find twice when retrieving deeply nested object in javascript

I am able to find the object in the javascript below but it is pretty horrible and I am doing the find twice.
I'd be interested in a better way and one that did not involve lodash which I am current not using.
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const selected = statuses.find(status => {
const none = status.items.find(alert => {
return alert.name === 'none';
});
return !!none;
});
console.log(selected)
const a = selected.items.find(s => s.name === 'none');
console.log(a)
You could use a nested some and find like this. This way you can skip the the operation once a match is found.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
let found;
statuses.some(a => {
const s = a.items.find(i => i.name === "none");
if (s) {
found = s;
return true
} else {
return false
}
})
console.log(found)
You could do something like this using map, concat and find. This is a bit slower but looks neater.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
const found = [].concat(...statuses.map(a => a.items))
.find(a => a.name === "none")
console.log(found)
Here's a jsperf comparing it with your code. The first one is the fastest.
You could combine all the status items into one array with reduce() and then you only have to find() once:
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const theNones = statuses
.reduce(function(s, t) { return s.items.concat(t.items); })
.find(function(i) { return i.name === 'none'; });
console.log(theNones);
You could use flatMap and find:
Note: Array.prototype.flatMap is in Stage 3 and not part of the language yet but ships in most environments today (including Babel).
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr
.flatMap(({ items }) => items)
.find(({
name
}) => name === 'none')
)
We could polyfill the flatting with concating (via map + reduce):
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
console.log(
arr
.map(({ items }) => items)
.reduce((items1, items2) => items1.concat(items2))
.find(({
name
}) => name === 'none')
)
You could also reduce the result:
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr.reduce((result, {
items
}) =>
result ? result : items.find(({
name
}) => name === 'none'), null)
)
One reduce function (one iteration over everything), that returns an array of any object matching the criteria:
const [firstOne, ...others] = statuses.reduce((found, group) => found.concat(group.items.filter(item => item.name === 'none')), [])
I used destructuring to mimic your find idea, as you seem chiefly interested in the first one you come across. Because this iterates only once over each item, it is better than the alternative answers in terms of performance, but if you are really concerned for performance, then a for loop is your best bet, as the return will short-circuit the function and give you your value:
const findFirstMatchByName = (name) => {
for (let group of statuses) {
for (let item of group.items) {
if (item.name === name) {
return item
}
}
}
}
findFirstMatchByName('none')

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