问题
groupSelectedQuestions(selectedQuestions){
var questions = [
{ q: 'why?', group: 'no-group', date: '1' },
{ q: 'what?', group: 'group 1', date: '1' },
{ q: 'when?', group: 'group 2', date: '2' },
{ q: 'where?', group: 'group 1', date: '2' },
{ q: 'which', group: 'group 2', date: '3' }
],
result = questions.reduce(function (r, a) {
r[a.group] = r[a.group] || [];
r[a.group].push(a);
return r;
}, {});
/**
more code here.
Here I would put if statements that check for all condtions
I have stated in the question below
*/
}
I am trying to make a function that users call and groups questions in certain groups. Above you can see part of the code I have come up with.
I have a number of conditions on how I want to group the questions.
- No group can have less than two number of questions.
'no-group'
means the question is not in any group. So'no-group'
can be just one question or all of them.- Groups should be assigned by the earliest date in the questions of that group. For example
'group 1'
earliest(in terms of date) question should be earlier than the earliest question in 'group 2'
and this should also be readjusted when questions are regrouped or if a question is removed from a group. - Grouped questions can be regrouped. And when doing so if any question is left in a group alone it should marked as
'no-group'
. - When assigning groups when
'group 1'
is taken assign'group 2'
, when'group 2'
is taken assign'group 3'
and so on.
The way I would do it is use if statements. But since the array of questions can have up to twenty questions and the groups can go from 'group 1'
, 'group 2'
... to 'group 20'
, the number of if statements will become many.
I made a stackblitz to communicate better what I am trying to achieve. Is there a way I can use recursion to achieve what I want to achieve and avoid many if statements?
If there is something that is not clear kindly ask I will be glad to make it clear.
The code in the stackblitz is as follows (it is an Angular stackblitz):
the controller
questions = [
{ _id:1, q: 'why?', group: 'no-group', date: '1', selected:false },
{ _id:2, q: 'what?', group: 'group 1', date: '1', selected:false },
{ _id:3, q: 'when?', group: 'group 2', date: '2', selected:false },
{ _id:4, q: 'where?', group: 'group 1', date: '2', selected:false },
{ _id:5, q: 'which?', group: 'group 2', date: '3', selected:false }
];
selectOrUnselectQuestion(question){
let newQuestions = this.questions.map(newQuestion => {
if(newQuestion._id === question._id){
if(!newQuestion.selected){
newQuestion.selected = true;
} else {
newQuestion.selected = false;
}
return newQuestion;
} else {
return newQuestion;
}
})
this.questions = newQuestions;
}
groupSelectedQuestions(){
let selectedQuestions = this.questions.filter(q => q.selected);
let selectedQuestionIds = selectedQuestions.map(selectedQuestion=>{ return selectedQuestion._id; })
let newQuestions = this.questions.map(question => {
if(selectedQuestions.length==1 && selectedQuestionIds.includes(question._id)){
question.group = 'no-group';
question.selected = false;
return question
} else
if(selectedQuestions.length>1 && selectedQuestionIds.includes(question._id)){
question.group = 'group 1';
question.selected = false;
return question
} else {
return question;
}
})
this.questions = newQuestions;
// deselect selected questions
}
the view:
<div style="text-align:center">Questions</div>
<div style="text-align:center; padding:10px;">
<div *ngFor="let question of questions" (click)="selectOrUnselectQuestion(question)"
[ngClass]="{'selected': question.selected}" class="question">
<span style="padding-right:10px">{{question.q}}</span>
<span>{{question.group}}</span>
</div>
<button (click)="groupSelectedQuestions()" style="margin:10px 0" type="button">
group selected questions
</button>
</div>
回答1:
I'm afraid that the discussion in the comments did not do much to help me understand.
Here is an attempt that still guesses at some of your requirements:
// utility functions
const groupBy = (prop) => (xs) =>
xs .reduce (
(a, {[prop]: p, ...rest}) => ({...a, [p]: [...(a[p] || []), rest]}),
{}
)
const partition = (pred) => (xs) =>
xs .reduce (([yes, no], x) => pred (x) ? [[...yes, x], no] : [yes, [...no, x]], [[], []])
// main function
const makeGroups = questions => {
const {'no-group': groupless, ...rest} = groupBy ('group') (questions)
const [largeEnough, tooSmall] = partition ((v) => v.length > 1) (Object .values (rest))
const noGroup = [...groupless, ...tooSmall.flat()].sort((a, b) => a.date - b.date)
return {
...Object .fromEntries (
largeEnough
.map (group => group.sort ((a, b) => a .date - b .date))
.sort ((group1, group2) => group1 [0] .date - group2 [0] .date)
.map ((group, i) => [`group ${i + 1}`, group])
),
'no-group': noGroup
}
}
// sample data
const questions = [
{_id: 1, q: 'why?', group: 'no-group', date: '8', selected: false },
{_id: 2, q: 'what?', group: 'A', date: '6', selected: false },
{_id: 3, q: 'when?', group: 'C', date: '7', selected: false },
{_id: 4, q: 'where?', group: 'A', date: '5', selected: false },
{_id: 5, q: 'which?', group: 'B', date: '3', selected: false },
{_id: 6, q: 'who?', date: '0', selected: false }, // no group supplied so will end up in no-group
{_id: 7, q: 'why not?', group: 'B', date: '9', selected: false },
{_id: 8, q: 'who, me?', group: 'A', date: '4', selected: false },
{_id: 9, q: 'where is waldo?', group: 'A', date: '1', selected: false },
{_id: 10, q: 'which way is up?', group: 'B', date: '2', selected: false },
{_id: 11, q: 'when is lunch?', group: 'D', date: '10', selected: false },
];
// demo
console .log (makeGroups (questions))
.as-console-wrapper {max-height: 100% !important; top: 0}
The output will look like this:
{
'group 1': [
{_id: 9, q: "where is waldo?", date: "1", selected: false},
{_id: 8, q: "who, me?", date: "4", selected: false},
{_id: 4, q: "where?", date: "5", selected: false},
{_id: 2, q: "what?", date: "6", selected: false}
],
'group 2': [
{_id: 10, q: "which way is up?", date: "2", selected: false},
{_id: 5, q: "which?", date: "3", selected: false},
{_id: 7, q: "why not?", date: "9", selected: false}
],
'no-group': [
{_id: 6, q: "who?", date: "0", selected: false},
{_id: 3, q: "when?", date: "7", selected: false},
{_id: 1, q: "why?", date: "8", selected: false},
{_id: 11, q: "when is lunch?", date: "10",selected: false}
]
}
The groups are internally sorted by date, and the groups are sorted between them by the first date in their list. Any group without at least two entries is folded into no-group
and the group numbers are assigned sequentially.
The big question is whether this fits your needs. If not, can you show a sample input and expected output as I have done here?
Update: inlining helpers
Although I'm a big fan of helper functions and I use that partition
occasionally and a slightly generalized version of that groupBy
quite frequently, I'd like to point out that as each is only used once, we could inline them quite simply:
const makeGroups = questions => {
const {'no-group': groupless, ...rest} = questions .reduce (
(a, {group = 'no-group', ...rest}) => ({...a, [group]: [...(a[group] || []), rest]}),
{}
)
const [largeEnough, tooSmall] = Object .values (rest) .reduce (
([yes, no], x) => x.length > 1 ? [[...yes, x], no] : [yes, [...no, x]], [[], []]
)
const noGroup = [...groupless, ...tooSmall.flat()].sort((a, b) => a.date - b.date)
return {
...Object .fromEntries (
largeEnough
.map (group => group.sort ((a, b) => a .date - b .date))
.sort ((group1, group2) => group1 [0] .date - group2 [0] .date)
.map ((group, i) => [`group ${i + 1}`, group])
),
'no-group': noGroup
}
}
来源:https://stackoverflow.com/questions/65184098/how-to-group-objects-in-an-array-and-sort-them-depending-on-an-object-property