extract id from array using map with condition javascript - javascript

There is an array of objects
const groups = [
{ id: 0, name: "All", selected: false },
{ id: -1, name: "All", selected: true },
{ id: 1, name: "Group1", selected: false },
{ id: 2, name: "Group2", selected: false },
{ id: 3, name: "Group3", selected: false },
{ id: 4, name: "Group4", selected: true }
];
I want to extract ids from this object with map
groups.map(group => group.id > 0 && group.selected ? group.id:null)
but the result will be
[null,null,4,null...]
actually it should be [4]
I know I can use another function like forEach and push or map and filter but I would solve it with one iteration with map or something else.

Filter the object/s under your criteria and then extract the id/s with a map
const groups = [{
id: 0,
name: "All",
selected: false
},
{
id: -1,
name: "All",
selected: true
},
{
id: 1,
name: "Group1",
selected: false
},
{
id: 2,
name: "Group2",
selected: false
},
{
id: 3,
name: "Group3",
selected: false
},
{
id: 4,
name: "Group4",
selected: true
}
];
const result = groups.filter(x => x.id > 0 && x.selected).map(x => x.id)
console.log(result)

you can use a transducer in this case, so that you will not iterate through the array 2 times.
const groups = [
{ id: 0, name: "All", selected: false },
{ id: -1, name: "All", selected: true },
{ id: 1, name: "Group1", selected: false },
{ id: 2, name: "Group2", selected: false },
{ id: 3, name: "Group3", selected: false },
{ id: 4, name: "Group4", selected: true }
];
const filteredIds = groups.reduce(
(ids, { id, selected }) => (
id > 0 && selected ? [...ids, id] : ids
), []
);
console.log(filteredIds);

The map() method creates a new array with the results of calling a function for every array element and extraction is not possible with this. Either use map() and then discard the array items or use filter().
Better approach would be using filter(). The filter() method creates an array filled with all array elements that pass a test (provided as a function).
let result = groups.filter(x => x.id > 0 && x.selected).map(x => x.id)

You can easily do this in one iteration with transducers.
const getPositiveSelectedIDs = pipe([
filter(and([
gt(get('id'), 0),
get('selected'),
])),
map(get('id')),
])
transform(getPositiveSelectedIDs, [])(groups) // => [4]
In this example, getPositiveSelectedIDs is a transducer we declared using functions pipe, map, and filter. The predicate passed to filter uses functions and, gt, and get to say
only let groups through who have positive ids and who have been selected
then, without creating any intermediate arrays, we get the ids of each group with map and get. getPositiveSelectedIDs behaves as a transducer when we use it in transform. The flow of data starts when we call the transformation transform(getPositiveSelectedIDs, []) with groups.
More on transducers

Related

Filter array of objects dynamically according to another array of objects

So I am making a filter functionality for React, so I have an array of objects, and based on another array that contains values to filter the array, I need to get the filtered values.
code: the array of objects to apply the filter to:
const citiesData = [
{
id: 1,
name: 'amritsar',
popu: '1200'
},
{
id: 2,
name: 'jalandhar',
popu: '1300'
},
{
id: 3,
name: 'phagwara',
popu: '1200'
},
{
id: 4,
name: 'ludhiana',
popu: '1400'
},
{
id: 5,
name: 'mumbai',
popu: '2000'
},
{
id: 6,
name: 'banglore',
popu: '2000'
},
{
id: 7,
name: 'ohter city 1',
popu: '1500'
},
{
id: 8,
name: 'ohter city 2',
popu: '1500'
},
{
id: 9,
name: 'anohter city 1',
popu: '2200'
},
{
id: 10,
name: 'anohter city 2',
popu: '2200'
},
]
code: filters array based on what I need to apply the conditions:
const filterCity = [
{
filterType: 'name',
filterValue: 'amritsar'
},
{
filterType: 'popu',
filterValue: '1200'
}
]
solutions I've tried:-
code: solution 1:
const filteredList = citiesData.filter(item => {
return filterCity.filter(fItem => item[fItem.filterType] === fItem.filterValue).length
})
code: solution 2:
const filteredList = citiesData.filter(item => {
return filterCity.reduce((acc, val) => {
if(item[val.filterType] === val.filterValue) {
acc = true
}
return acc;
}, false)
})
code: result I'm getting:
[
{ id: 1, name: 'amritsar', popu: '1200' },
{ id: 3, name: 'phagwara', popu: '1200' }
]
it's giving me two objects because according to the filters array I'm searching for the name and popu fields. but the expected result should be:
[ { id: 1, name: 'amritsar', popu: '1200' } ]
because the name and popu is similar in that but in the second object the name is not the same.
I want the code to check all the conditions and then give me the result. right now it's working on the individual filter and individual array item.
so can anyone help me on this!!
so, it should be an AND filter (combining all conditions)?
res = citiesData.filter(d =>
filterCity.every(f => d[f.filterType] === f.filterValue))
for the OR filter (any condition), replace every with some.

Updating complicated Object with useState

I'm trying to do a set State function and change only one value.
This is the object and for example, I want to change book1 in index [0] (so name1) from true to false.
I can't understand how to do it
everything that I'm trying simply overwrites part of the object
{
book1: [
{ name: 1, selected: true },
{ name: 2, selected: false },
{ name: 3, selected: false },
{ name: 4, selected: false },
{ name: 5, selected: true },
],
book2: [
{ name: 1, selected: false },
{ name: 2, selected: false },
{ name: 3, selected: true },
{ name: 4, selected: false },
{ name: 5, selected: true },
],
}
Because the object is in state, you can't modify it directly. Instead, you have to create a copy of the object and any inner objects (including arrays) that you change. See comments:
// Use the callback form because you're updating state based on existing state
setTheObject(original => {
// Shallow copy the object and book1
const update = {...original, book1: [...original.book1]};
// Replace the object at index 0 with a new updated object
update.book1[0] = {...update.book1[0], selected: false};
// return the update
return update;
});
It is technically possible to do this in one nested operation, but it's much harder to read and debug:
// Use the callback form because you're updating state based on existing state
setTheObject(original => ({
// Shallow copy the object
...original,
// Shallow copy `book1` while updating index 0 with a copy of the object with the updated property
book1: Object.assign([], original.book1, {0: {...original.book1[0], selected: false}}),
}));
I don't recommend it. :-D
const [books, setBooks] = useState({
book1: [
{ name: 1, selected: true },
{ name: 2, selected: false },
{ name: 3, selected: false },
{ name: 4, selected: false },
{ name: 5, selected: true }
],
book2: [
{ name: 1, selected: false },
{ name: 2, selected: false },
{ name: 3, selected: true },
{ name: 4, selected: false },
{ name: 5, selected: true }
]
});
Assume you hold the books state like this. Now update the books state like this.
setBooks({
...books,
book1: books.book1.map((book) =>
book.name === 1 ? { ...book, selected: !book.selected } : book
)
});
In order to check the effect after state is changed, you can add dependancy to useEffect hook and as soon the books are changed, you will see the updated state in useEffect.
useEffect(() => {
console.log(books);
}, [books]);

Find Duplicate Object in List and Add Parameters

I am trying to find duplicate objects in a list of objects and add new parameters to the duplicate one.
Below snipped code is what I implemented so far. The problem is that it adds desired parameters to every object in the list.
const list = [{
id: 1,
name: 'test1'
},
{
id: 2,
name: 'test2'
},
{
id: 3,
name: 'test3'
},
{
id: 2,
name: 'test2'
}
];
const newList = list.reduce(
(unique, item) => (unique.includes(item) ? unique : [...unique, {
...item,
duplicated: true,
name: `${item.name}_${item.id}`
}]), []
);
console.log(newList);
Since there are two duplicate objects by id, the duplicated one should have duplicated and new name parameters. What part is wrong in my implementation?
By using findIndex method:
const list = [{
id: 1,
name: 'test1'
},
{
id: 2,
name: 'test2'
},
{
id: 3,
name: 'test3'
},
{
id: 2,
name: 'test2'
}
];
const newList = list.reduce(
(unique, item) => (unique.findIndex(x => x.id === item.id) > -1 ? [...unique, {
...item,
duplicated: true,
name: `${item.name}_${item.id}`
}] : [...unique, item]), []);
console.log(newList);
It can be written simply:
const
list = [
{ id: 1, name: 'test1' },
{ id: 2, name: 'test2' },
{ id: 3, name: 'test3' },
{ id: 2, name: 'test2' }
],
uniqueList = list.reduce((arr, { id, name }) =>
arr.concat({
id,
name,
...arr.some(item => id === item.id) && { duplicate: true, name: `${name}_${id}` }
}), []);
console.log(uniqueList);
The problem was that when you called includes you were actually looking for an object whose pointer exists in the array.
In order to find an object which has property are the same as a requested property, you have no choice but to use functions such as some or every that is different from includes - you can send them a callback and not just an object.

ReactJS - Swap out all the values in an array of objects in state with another array

I am trying to swap out all the values of an array of objects in state with a whole new array of objects. However, nothing seems to be working. I've tried the following:
const list1 = [
{ id: 1, name: 'item1' },
{ id: 2, name: 'item1' },
{ id: 3, name: 'item1' },
{ id: 4, name: 'item1' },
]
const list2 = [
{ id: 1, name: 'newItem1' },
{ id: 2, name: 'newItem2' },
{ id: 3, name: 'newItem3' },
{ id: 4, name: 'newItem4' },
]
class FindTab extends Component {
state = {
status: 'loading',
location: null,
view: this.props.view,
map: this.props.map,
locationValues: list1,
}
}
this.setState(prevState => ({
locationValues: [ ...prevState.locationValues, list2 ],
}))
or just simpler:
this.setState(locationValues: list2)
Neither seem to work. Is there any guidance as to how one should replace an array of objects with another array for a state property?
You could spread the array in a new one like:
const locationValues = [ ...state.locationValues, ...list2 ]
this.setState({ locationValues })

Find Ids from multi dimensional object array

I have a multi dimensional object array:
var products = [
{ id: 1, groups: [ { id: 1.1, selected: true }, { id: 1.2, selected: false }},
{ id: 2, groups: [ { id: 2.1, selected: false }, { id: 2.2, selected: true }} ];
How can I find list of selected groups (group with selected flag set to true) in a single dimensional array using ES6.
Expected Result:
[1.1, 2.2]
You could use reduce method and inside one more forEach loop to get group objects where selected is true.
var products = [
{ id: 1, groups: [ { id: 1.1, selected: true }, { id: 1.2, selected: false }]},
{ id: 2, groups: [ { id: 2.1, selected: false }, { id: 2.2, selected: true }]} ];
const result = products.reduce((r, {groups}) => {
groups.forEach(e => e.selected && r.push(e.id));
return r;
}, [])
console.log(result)
A possible alternative to all the other answers.
Flatten the array, then extract the required info.
const products = [
{ id: 1, groups: [{ id: 1.1, selected: true }, { id: 1.2, selected: false }] },
{ id: 2, groups: [{ id: 2.1, selected: false }, { id: 2.2, selected: true }] }];
const allIds = [].concat(...products.map(p => p.groups)).filter(g => g.selected).map(x => x.id)
console.log(allIds)
You can use reduce and filter like below, this will give you all ids that have selected: true. Note that I changed id: 1.2 to be true to demonstrate the ability of getting multiple true results.
const products = [
{ id: 1, groups: [ { id: 1.1, selected: true }, { id: 1.2, selected: true } ]},
{ id: 2, groups: [ { id: 2.1, selected: false }, { id: 2.2, selected: true } ]} ];
const res = products.reduce((a, {groups}) =>
a.concat(...groups.filter(({selected}) => selected).map(({id}) => id))
, []);
console.log(res);
var products = [
{ id: 1, groups: [ { id: 1.1, selected: true }, { id: 1.2, selected: false }]},
{ id: 2, groups: [ { id: 2.1, selected: false }, { id: 2.2, selected: true } ]}];
var desiredResult = products.map(product => {
return product.groups.filter(group => group.selected).map(item => item.id).toString();
});
console.log(desiredResult);
you can make use of map,filter and concat function and get result
var products = [
{ id: 1, groups: [ { id: 1.1, selected: true }, { id: 1.3, selected: true },{ id: 1.2, selected: false }]},
{ id: 2, groups: [ { id: 2.1, selected: false }, { id: 2.2, selected: true }]} ];
const selectgroups = [].concat(...products.map(x => x.groups.filter(g=> g.selected)));
const selectedGroupIds = selectgroups.map(g=> g.id);
console.log(selectedGroupIds);
code which provide list of all selected groups
Working Demo
I see a completely different approach: using specialized data structures to model the data the way you want to work with it:
// Store product id mapped to a set of
// group ids
const productGroupMap = new Map ( [
[ 1, new Set ( [ 1.1, 1.2 ] ) ],
[ 2, new Set ( [ 2.1, 2.2 ] ) ]
] )
// Store group ids to their data
const groupMap = new Map ( [
[ 1.1, { title: "Title 1.1" } ],
[ 1.2, { title: "Title 1.2" } ],
[ 2.1, { title: "Title 2.1" } ],
[ 2.2, { title: "Title 2.2" } ]
] )
// Somewhere in your application, you would declare
// a set of selected group ids, and you would add ids
// when the user selects a given group
const selectedGroups = new Set ()
selectedGroups.add ( 1.1 )
selectedGroups.add ( 2.2 )
// Finally, when you want these selected groups, you just need to
// map selected group ids to their group data getting it from the
// groupMap
const groups = [ ...selectedGroups ].map ( id => groupMap.get ( id ) )
console.log ( groups )

Categories

Resources