I want to delete objects that do not have a specific array - javascript

console.log(value)
{
List: [
{
id: 1
title: 'hi',
content: [
{
id: 1,
functionId: 11,
}
]
}
{
id: 2,
title: 'minsu',
content: []
}
]
}
I want to delete the object if there is no content array.
I am creating a variable called control and outputting it using the map function inside.
If the objects without content array have to be deleted by applying a filter condition, I don't know how to do it.
let control: any[any] = [
value.map((value: any) => {
return {
id: value?.id,
title: value?.title,
content: value?.content[0].functionId
}
})
]
But now, when I do console.log(control), an error occurs because the second object does not have content.
I want to delete the second object without content and print it like this.
console.log(control)
[
id: 1,
title: 'hi',
functionId: 11
]

First filter out the elements with 0 length content array or content array doesn't exists and then map to transform it
const List = [ { id: 1, title: 'hi', content: [ { id: 1, functionId: 11, } ] }, { id: 2, title: 'minsu', content: [] } ]
const control = List
.filter(({content}) => content?.length)
.map(({id,title,content:[{functionId}]}) => ({id,title,functionId}))
console.log(control)

Filter source list the way you need and reapply filtered list as new value
const sourceObject = {
list: [
{
id: 1,
title: 'hi',
content: [
{
id: 1,
functionId: 11,
}
]
},
{
id: 2,
title: 'minsu',
content: []
}
]
};
const filteredList = sourceObject.list.filter(element => (
typeof(element.content) === 'object' && element.content.length > 0
));
sourceObject.list = filteredList;
console.log(sourceObject);

I usually use reduce to solve problems like this
const items = [
{
id: 1,
title: 'hi',
content: [
{
id: 1,
functionId: 11,
}
]
},
{
id: 2,
title: 'minsu',
content: []
}
];
const result = items.reduce((rs, item) => {
const functionId = item?.content?.[0]?.functionId;
if (functionId) {
rs.push({
id: item.id,
title: item.title,
functionId
})
}
return rs;
}, [])
console.log(result)

Related

Advanced filtering nested elements in TypeScript/JavaScript

Given the following structure and data:
interface GrandChild {
id: number,
values: Array<string>,
}
interface Child {
id: number,
subItems: Array<GrandChild>
}
interface Foo {
items: Array<Child>
}
const data: Foo = {
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 2, subItems: [ { id: 20, values: ['REMOVE', 'REMOVE'] }, { id: 21, values: ['REMOVE'] } ] },
{ id: 3, subItems: [ { id: 30, values: ['REMOVE'] }, { id: 31, values: ['REMOVE'] }, { id: 32, values: ['REMOVE', '32'] } ] },
]
};
How can I use the Array's methods (filter, map, some, etc.) to achieve the following result?
const expected: Foo = {
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 3, subItems: [ { id: 32, values: ['32'] } ] },
]
}
So far, I filtered the resulting data, removing the undesired elements, as following:
const filteredData: Foo = {
...data,
items: data.items.map(item => ({
...item,
subItems: item.subItems.map(subItem => ({
...subItem,
values: subItem.values.filter(value => value !== 'REMOVE')
}))
}))
}
Resulting:
{
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 2, subItems: [ { id: 20, values: [] }, { id: 21, values: [] } ] },
{ id: 3, subItems: [ { id: 30, values: [] }, { id: 31, values: [] }, { id: 32, values: ['32'] } ] },
]
};
But, I cannot figure a way out to remove the empty subItems elements without looping through the result.
You can check online the above code here.
If you really want to do it just with filter and map, add a filter after each of your maps to remove subItems that have an empty values array and to remove items that have an empty subItems array:
const filteredData = {
...data,
items: data.items
.map((item) => ({
...item,
subItems: item.subItems
.map((subItem) => ({
...subItem,
values: subItem.values.filter((value) => value !== "REMOVE"),
}))
.filter(({ values }) => values.length > 0), // ***
}))
.filter(({subItems}) => subItems.length > 0), // ***
};
But:
When I have map followed by filter, I always ask myself if the data is large enough that I should avoid making multiple passes through it.
When I'm doing lots of nesting of map calls and such, I always ask myself if it would be clearer when reading the code later to use simpler, smaller loops.
Here's what you might do if answering "yes" to either or both of those questions:
const filteredData: Foo = {
...data,
items: [],
};
for (const item of data.items) {
const subItems: Array<GrandChild> = [];
for (const subItem of item.subItems) {
const values = subItem.values.filter((value) => value !== "REMOVE");
if (values.length) {
subItems.push({
...subItem,
values,
});
}
}
if (subItems.length > 0) {
filteredData.items.push({
...item,
subItems,
});
}
}

How to deep filter in array with objects in JS

I have an array and want to get just object { id: 4, name: 'name4' },
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
},
I try in this way.
const name = 'name4';
example.forEach((item) => item.items.find((i) => i.name === name));
But get undefined.
You can using flatMap() to do it
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
}]
const name = 'name4';
let result = example.flatMap(e => e.items).filter(d => d.name == name)
console.log(result)
this way...?
const example =
[ { id : '1234'
, desc : 'sample1'
, items:
[ { id: 1, name: 'name1' }
, { id: 2, name: 'testItem2' }
] }
, { id : '3456'
, desc : 'sample2'
, items:
[ { id: 4, name: 'name4' }
, { id: 5, name: 'testItem5' }
] } ];
const rechName = (s,arr) =>
arr.find( x => // find the first parent object
x.items.some( y => // containing the search
y.name === s )
)?.items // if one
.find( z => z.name === s ); // find it in!
console.log( rechName('name4', example) ) // -> { id: 4, name: 'name4' }
console.log( rechName('abc', example) ) // -> undefined
forEach doesn't do what you think it does. From the docs:
The forEach() method executes a provided function once for each array element.
...
Return value
undefined.
So if you want to use forEach you need to save the value:
const example =
[ { id : '1234'
, desc : 'sample1'
, items:
[ { id: 1, name: 'name1' }
, { id: 2, name: 'testItem2' }
] }
, { id : '3456'
, desc : 'sample2'
, items:
[ { id: 4, name: 'name4' }
, { id: 5, name: 'testItem5' }
] } ]
const results = []; // Store matches here
const name = 'name4';
example.forEach((item) => {
const res = item.items.find((i) => i.name === name);
if (res !== undefined) {
results.push(res);
}
});
console.log(results);
IMHO I would suggest a more functional approach using flatMap and filter instead of forEach.
Lastly, note that in my above snippet, I'm storing the results in an array as it's not entirely clear to me that you won't have multiple matches per your example. But if you're sure that you will only ever have one result then a simple for loop works better, especially if you have a large array of items:
let result = null;
for (let i = 0; i < example.length; i++) {
const res = example[i].items.find((j) => j.name === name);
if (res !== undefined) {
result = res;
break; // No need to iterate further
}
}
console.log(result);
You could use a recursive search function. Here's a detailed example:
// Applies the function recursively from the top of the data tree ("depth-first")
const
data = getData(),
id = 4,
result = findById(data, id);
console.log(result ?? `No item with id ${id} found`);
// Defines the function
function findById(haystack, needleId){
let needle = null; // Defaults to null if no match at or below this level
// Iterates through all the items at this level
for(const item of haystack){
if(item.id == needleId){
// Qapla': Quits early, passes honorable item up to previous level
needle = item;
return needle;
}
else {
// Checks children, grandchildren, etc, before continuing iteration
const nextHaystack = item.items;
if(nextHaystack?.length){
needle = findById(nextHaystack, needleId); // Recursive call
}
}
// Done searching children, continues to next iteration at this level
}
// Done searching this level, returns result up to previous level
return needle;
}
// Gets the initial data
function getData(){
return [
{
id: '1234',
desc: 'sample1',
items: [ { id: 1, name: 'name1' }, { id: 2, name: 'testItem2' } ]
},
{
id: '3456',
desc: 'sample2',
items: [ { id: 4, name: 'name4' }, { id: 5, name: 'testItem5' } ]
}
];
}

How to change value in array with objects in JS

I have an array and want to change name in object { id: 4, name: 'name4' } to 'name6'
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' }
]
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' }
]
},
I try in this way but it isn't working
const name = 'name4';
const result = example?.forEach((group) =>
group.items.forEach((item) =>
if (item.name === name) {
return item.name === 'name6';
}
return null;
})
);
The for...of statement is my recommendation for readability and loop optimisation.
const example = [
{
id: '1234',
desc: 'sample1',
items: [
{ id: 1, name: 'name1' },
{ id: 2, name: 'testItem2' },
],
},
{
id: '3456',
desc: 'sample2',
items: [
{ id: 4, name: 'name4' },
{ id: 5, name: 'testItem5' },
],
},
];
const oldName = 'name4';
const newName = 'name6';
for (const group of example) {
for (const item of group.items) {
if (item.name === oldName) {
item.name === newName;
break
}
}
}
You could even go a step further and terminate the outer loop with a label if you only need to change the name in a single group.
outerLoop: for (const group of example) {
for (const item of group.items) {
if (item.name === oldName) {
item.name === newName;
break outerLoop;
}
}
}
Hope this helps.
You could either change the value by simply assigning a new value.
example[1].items[0].name = 'name6'
But you can also iterate through all items and search for the name you want to change. I created a function that goes through an array and loops over its nested items arrays searching for any given name (targetName) and replacing it with a new one (newName):
function changeName(array, targetName, newName) {
// Loop through the elements of array
array.forEach((element) => {
// Check each item: change the name if it matches the target
element.items.forEach((item) => {
if (item.name === targetName) item.name = newName;
});
});
}
// This function will check example array and change
// every name that has a value 'name4' into 'name6'
changeName(example, "name4", "name6");
forEach doesn't return any value.
Instead of return item.name === 'name6' you can simply set new value to item.name.
Why not like this?
const example = [{
id: '1234',
desc: 'sample1',
items: [{
id: 1,
name: 'name1'
},
{
id: 2,
name: 'testItem2'
}
]
},
{
id: '3456',
desc: 'sample2',
items: [{
id: 4,
name: 'name4'
},
{
id: 5,
name: 'testItem5'
}
]
},
]
example[1].items[0].name = 'name6'
console.log(example)

Filter an object iside an array

I need to delete entire object that do not have passed
here is the array
const array = [{
course: 1,
list: [{
id: 1,
name: "john",
code: true
},
{
id: 1,
name: "maria",
code: true
},
]
},
{
course: 2,
list: [{
id: 3,
name: "rose"
},
{
id: 4,
name: "mark",
code: true
}
]
}
]
That i need is remove obj that not have code:true, and get this
const array = [{
course: 1,
list: [{
id: 1,
name: "john",
code: true
}, ]
},
{
course: 2,
list: [{
id: 1,
name: "mark",
code: true
}]
}
]
I tried to make a map inside a filter, but it does not work at all
const remove = array.filter(function(lines) {
return lines.map(line => line.list.map(list => list.code))
});
You can map through the array, then copy all properties of the specific item and separately do the filtering on the list attribute.
const array = [{
course: 1,
list: [{
id: 1,
name: "john",
code: true
},
{
id: 1,
name: "maria",
code: true
},
]
},
{
course: 2,
list: [{
id: 3,
name: "rose"
},
{
id: 4,
name: "mark",
code: true
}
]
}
]
const filter = arr => arr.map(arrItem => ({
...arrItem,
list: arrItem.list.filter( listItem => listItem.code )
})
)
console.log( filter(array) )
const filtered = [];
arr.forEach(item => {
const list = item.list.filter(listItem => listItem.code);
if(list.length > 0) {
filter.push({ ...item, list });
}
});
This approach will only add items to the filtered output array if the list contains any items after filtering out those with code: false. To include them anyway, you could do:
const filtered = arr.map(item => ({
...item,
list: item.list.filter(listItem => listItem.code)
});

Return deconstructed array in object with es6 map

I want to return a deconsturcted array so I only get single element in te returned array instead of array.
const data = [
{
title: 'amsterdam',
components: [
{
id: 1,
name: 'yanick',
},
{
id: 2,
name: 'ronald',
},
],
},
{
title: 'rotterdam',
components: [
{
id: 4,
name: 'nicky',
},
{
id: 3,
name: 'casper',
},
],
},
];
const test = data
.map(item => {
console.log(item.components);
return item.components;
}).map(array => {
// how to get comibned components here?
// it can't use ...item.components (deconstructing or something)
});
console.log('test', test);
So I want to use chained map functions to create one array of all elements in item.components. Is this possible? Seems like I can't deconstruct the array of each item.
Array.prototype.reduce seems like the correct method to use in this context.
const test = data.reduce( (result, current) => result.concat(current.components) , []);
console.log('test', test);
Output
test [ { id: 1, name: 'yanick' },
{ id: 2, name: 'ronald' },
{ id: 4, name: 'nicky' },
{ id: 3, name: 'casper' } ]
Get the components with Array.map(), and flatten by spreading into Array.concat():
const data = [{"title":"amsterdam","components":[{"id":1,"name":"yanick"},{"id":2,"name":"ronald"}]},{"title":"rotterdam","components":[{"id":4,"name":"nicky"},{"id":3,"name":"casper"}]}];
const result = [].concat(...data.map(o => o.components));
console.log(result);
To get data combined into single array you can use reduce in combination with concat that will create a single array of results.
const data = [
{
title: 'amsterdam',
components: [
{
id: 1,
name: 'yanick',
},
{
id: 2,
name: 'ronald',
},
],
},
{
title: 'rotterdam',
components: [
{
id: 4,
name: 'nicky',
},
{
id: 3,
name: 'casper',
},
],
},
];
const test = data
.map(item => {
return item.components;
}).reduce((res, item) => {
return res.concat(item);
}, []);
console.log('test', test);

Categories