0

I have an array that looks something like this

var categorizedBooks = [
{
 name: "category1",
 books:[
        {name:"book1", bookContainerId: 1, active: true},
        {name:"book2", bookContainerId: 2, active: false},
        {name:"book3", bookContainerId: 2, active: true},
       ]
 },
{
 name: "category2",
 books:[
        {name:"book4", bookContainerId: 1, active: false},
        {name:"book5", bookContainerId: 3, active: true},
        {name:"book6", bookContainerId: 4, active: true},
       ]
 },
{
 name: "category3",
 books:[
        {name:"book7", bookContainerId: 1, active: false},
        {name:"book8", bookContainerId: 2, active: true},
        {name:"book9", bookContainerId: 4, active: false},
       ]
 },
{
 name: "category4",
 books:[
        {name:"book10", bookContainerId: 2, active: false},
        {name:"book11", bookContainerId: 2, active: false},
        {name:"book12", bookContainerId: 4, active: false},
       ]
 }

And i want to filter them on two arrays that looks something like this:

bookContainers: [1,3]
isActive: ['active', 'inactive] // an array of strings

These comes from inputs so it can change whenever. So i want to match bookContainers with their bookContainerId, and 'active'/'inactive' string array with their boolean active property and if a an object in categorizedBooks books array is empty i want to remove it.

So in this case i would get something like this (Desired output from the above):

{
 name: "category1",
 books:[
        {name:"book1", bookContainerId: 1, active: true}
       ]
 },
{
 name: "category2",
 books:[
        {name:"book4", bookContainerId: 1, active: false},
        {name:"book5", bookContainerId: 3, active: true}
       ]
 },
{
 name: "category3",
 books:[
        {name:"book7", bookContainerId: 1, active: false}
       ]
 }

Another problem is that i don't want to modify the original array because i want to be able to call it with new arrays to filter on whenever, and get the result. Here is my GET method now:

public get bookCategories(): Array<CategorizedBook> {
    let cloneToMapOf = [...this.categoriesAndBooks];

    const filteredCategories = cloneToMapOf.map(categorizedbooks => {
        categorizedbooks.books = categorizedbooks.properties.filter(book =>{
            return (rc.includes(books.bookContainerId) && 
sa.includes(book.active ? 'active' : 'inactive'));
        });
        return categorizedbooks;
    });

    const result = filteredCategories.filter(x => x.books.length > 0 && x.books != null);
    return result;
}

But for some reason this code modifies this.categoriesAndBooks so I becomes empty after time..

with the result I want to present in some HTML

    <nz-collapse [nzBordered]="false" *ngFor="let categorizedBook of bookCategories;">
        <nz-collapse-panel [nzHeader]="categorizedBook.name" [nzActive]="true">
            <ng-container *ngFor="let book of categorizedBook.properties; trackBy:trackBy.book">
                <ng-template [ngTemplateOutlet]="bookEditors" [ngTemplateOutletContext]="{book: book, form: bookValueForm}"></ng-template>
            </ng-container>
        </nz-collapse-panel>
    </nz-collapse>

I've been sitting with this for hours.. please help me

5
  • I did not understand the 'active' inactive part, can you explain it better ? Commented Oct 9, 2019 at 19:42
  • this is not clear could you please put the desired output and what did you tried Commented Oct 9, 2019 at 19:44
  • It's basically an array of strings that need to match the boolean true => 'active' Commented Oct 9, 2019 at 19:44
  • @G.aziz The desired output is there, i can add what I've tried. Commented Oct 9, 2019 at 19:45
  • @LittleMygler edit your and add th output you want to get from this obeject Commented Oct 9, 2019 at 19:45

2 Answers 2

1

Update

const filteredCategories = cloneToMapOf.map(categorizedbooks => {
        const books = categorizedbooks.properties.filter(book =>{
            return (rc.includes(books.bookContainerId) && 
sa.includes(book.active ? 'active' : 'inactive'));
        });
        return {...categorizedbooks,books}; // so that original array is not mutated.
    });

I have created a inner filter for books that are part of the container AND active state is one of the states in active array and then an outer filter for categories with empty book array

var categorizedBooks = [
{
 name: "category1",
 books:[
        {name:"book1", bookContainerId: 1, active: true},
        {name:"book2", bookContainerId: 2, active: false},
        {name:"book3", bookContainerId: 2, active: true},
       ]
 },
{
 name: "category2",
 books:[
        {name:"book4", bookContainerId: 1, active: false},
        {name:"book5", bookContainerId: 3, active: true},
        {name:"book6", bookContainerId: 4, active: true},
       ]
 },
{
 name: "category3",
 books:[
        {name:"book7", bookContainerId: 1, active: false},
        {name:"book8", bookContainerId: 2, active: true},
        {name:"book9", bookContainerId: 4, active: false},
       ]
 },
{
 name: "category4",
 books:[
        {name:"book10", bookContainerId: 2, active: false},
        {name:"book11", bookContainerId: 2, active: false},
        {name:"book12", bookContainerId: 4, active: false},
       ]
 }
 ];
var bookContainers = [1,3]
var isActive = ['active', 'inactive'];

var isActiveBoolState = isActive.map(state => state === 'active'); // helper to show which type of active state should be showed.

var result = categorizedBooks.map(category => ({...category, books: category.books.filter(book => bookContainers.includes( book.bookContainerId) && isActiveBoolState.includes(book.active)  )})).filter(category => category.books.length);

console.log(result);

Sign up to request clarification or add additional context in comments.

5 Comments

state === 'active' ? true : false is an anti-pattern, use state === 'active' instead.
updated, not sure if it is an anti-pattern though, thought it would be more readable. but it makes sense not to add the obvious
Problem with this is it makes a recursive thing with my html so it just runs and runs, check update for how i use it :)
I gets stuck in a recursive get, where the data updates and it fetches it again
@LittleMygler use the spread operator like I did in my answer. In your answere you are mutating the original object inside the array!
0

You could take the wanted keys in an array and add the values instead of some semantic values which needs to be converted to data types of the object for testing.

The result is an array with a filtered sub array and reassembled if the sub array contains some elements.

var categorizedBooks = [{ name: "category1", books: [{ name: "book1", bookContainerId: 1, active: true }, { name: "book2", bookContainerId: 2, active: false }, { name: "book3", bookContainerId: 2, active: true }] }, { name: "category2", books: [{ name: "book4", bookContainerId: 1, active: false }, { name: "book5", bookContainerId: 3, active: true }, { name: "book6", bookContainerId: 4, active: true }] }, { name: "category3", books: [{ name: "book7", bookContainerId: 1, active: false }, { name: "book8", bookContainerId: 2, active: true }, { name: "book9", bookContainerId: 4, active: false }] }, { name: "category4", books: [{ name: "book10", bookContainerId: 2, active: false }, { name: "book11", bookContainerId: 2, active: false }, { name: "book12", bookContainerId: 4, active: false }] }],
    filters = [['bookContainerId', [1, 3]], ['active', [true, false]]],
    result = categorizedBooks.reduce((r, { name, books }) => {
        books = books.filter(o => filters.every(([k, v]) => v.includes(o[k])));
        if (books.length) r.push({ name, books });
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

1 Comment

Nice code, but this also creates a recursive pattern when added into my *ngIf for some reason

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.