Find Ids from multi dimensional object array - javascript

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 )

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.

Find items by id's from another array and add to them new property

I want to find all objects in items array which is on groupedItems array by 'id' and add to each item isGrouped: true property.
const items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const groupedItems = [
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
so the result should be:
items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2", isGrouped:true },
{ id: 3, name: "item3", isGrouped: true }
];
any ideas ?
You can iterate over the items array using map, creating an isGrouped property which is the result of calling findIndex on the item.id property in the groupedItems id values:
const items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const groupedItems = [
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const result = items.map(item => ({
...item,
isGrouped : groupedItems.findIndex(g => g.id == item.id) >= 0
}
));
console.log(result);
Note that I've added isGrouped as false for those objects which are not grouped, rather than omitting the property. It seems that it should be easier to just test a boolean value of a property rather than checking whether the property exists. If you really want to omit the property, you could do something like this:
const items = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const groupedItems = [
{ id: 2, name: "item2" },
{ id: 3, name: "item3" }
];
const result = items.map(item =>
groupedItems.find(g => g.id == item.id) ? { ...item, isGrouped : true } : item
);
console.log(result);

Javascript - group array of objects by common values with label

I am trying to turn an array of objects into another array of objects by grouping by a specific value and adding that value as label and taking it out of the object in the new array.
Input: So for instance I have this array of objects:
let tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
Expected: I am looking to try and figure out how I can get to this, where there is a label for each of the unique groupings and options array containing the values of that grouping.
[
{
label: "Front End",
options: [
{ id: 1, value: "HTML" },
{ id: 2, value: "React" },
],
},
{
label: "Back End",
options: [
{ id: 3, value: "Node" },
{ id: 4, value: "PHP" },
],
},
]
The closest I have been able to get to is using reduce to group by the grouping key:
const groupedTech = tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = [];
}
// Grouping
acc[value.grouping].push(value);
return acc;
}, {});
Which gives me this:
{
"Front End": [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
],
"Back End": [
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
],
}
But this returns object not an array and doesn't remove the grouping value. I have not been able to figure out how to group properly because in the array of objects I have not found an efficient way to compare against to see if the grouping exists and if so add to that nested array. Would I be better off using something like .map()? Appreciate any leads/learnings!
You're very close, just wrap the key-value entries of the result you've got in a map function:
let tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
const groupedTech = Object.entries(
// What you have done
tech.reduce((acc, { id, grouping, value }) => {
// Group initialization
if (!acc[grouping]) {
acc[grouping] = [];
}
// Grouping
// FIX: only pushing the object that contains id and value
acc[grouping].push({ id, value });
return acc;
}, {})
).map(([label, options]) => ({ label, options }));
console.log(groupedTech);
You just have to do one more manipulation with Object.entries and .map
let tech = [
{ id: 1, grouping: 'Front End', value: 'HTML' },
{ id: 2, grouping: 'Front End', value: 'React' },
{ id: 3, grouping: 'Back End', value: 'Node' },
{ id: 4, grouping: 'Back End', value: 'PHP' }
]
const groupedTech = tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = []
}
// Grouping
acc[value.grouping].push(value)
return acc
}, {})
const res = Object.entries(groupedTech).map(([label, options]) => ({
label,
options
}))
console.log(res)
A minor variation on the other two answers if you want to get exactly the output you specify:
let tech = [{
id: 1,
grouping: "Front End",
value: "HTML"
},
{
id: 2,
grouping: "Front End",
value: "React"
},
{
id: 3,
grouping: "Back End",
value: "Node"
},
{
id: 4,
grouping: "Back End",
value: "PHP"
},
];
const groupedTech = Object.entries(
tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = [];
}
// Grouping
acc[value.grouping].push({
id: acc[value.grouping].length+1,
value: value.value
});
return acc;
}, {}))
.map(([label, options]) => ({
label,
options
}));
console.log(groupedTech);
I usually like to build up a Map of key / value pairs then transform those entries into the final result (usually using Array.prototype.map() or Array.from()).
const tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
const groupedMap = tech.reduce((map, { grouping, ...option }) => {
if (!map.has(grouping)) {
map.set(grouping, [])
}
map.get(grouping).push(option)
return map
}, new Map())
const groupedTech = Array.from(groupedMap, ([ label, options ]) => ({
label,
options
}))
console.log(groupedTech)
Using a Map and Map#values()
const grouped = tech.reduce((m,{grouping:label, ...rest})=>{
const group = m.get(label) || {label, options:[]};
group.options.push({...rest})
return m.set(label, group)
},new Map)
console.log([...grouped.values()])
<script>
let tech=[{id:1,grouping:"Front End",value:"HTML"},{id:2,grouping:"Front End",value:"React"},{id:3,grouping:"Back End",value:"Node"},{id:4,grouping:"Back End",value:"PHP"}];
</script>

extract id from array using map with condition 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

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 })

Categories

Resources