简体   繁体   中英

Filtering a JavaScript object with nested structure

let data = [
    {
        title: 'aa',
        releases: [
            { version: '1', owner: 'john'},
            { version: '2', owner: 'bob'}
        ]
    },
    {
        title: 'bb',
        releases: [
            { version: '1', owner: 'john'},
            { version: '2', owner: 'jack'}
        ]
    },
    {
        title: 'cc',
        releases: [
            { version: '1', owner: 'doo'},
            { version: '2', owner: 'park'}
        ]
    },
]

I have an array of objects that looks like above.

I am trying to filter the objects by the owner inside the releases array.

What I've tried is

data.filter(item => 
        ['john'].some(
            item.releases.some(
                obj=>obj.owner.toLowerCase().includes(['john'])
            )
        )
    )

I used an array of string(s) to have multiple search queries in the future. But this gives me an error

Uncaught TypeError: true is not a function

How can I solve this?

The result should be all objects that have john as an owner in any of its releases array

That ['john'].some( call isn't really helping. You have the logic already without that.

You are getting that error because you are passing the result of some() (which returns a boolean) to some() (which expect a function).

Also, when asking if a string includes a string, you should pass in a string, not an array.

const filteredData = data.filter(item => 
    item.releases.some(
        obj => obj.owner.toLowerCase().includes('john')
    )
)

some expects a function not value, in the outer most some you're just passing the value you need to pass function instead

data.filter(item =>  
        ['john'].some( () =>      //  <--- function
            item.releases.some(
                obj=>obj.owner.toLowerCase().includes('john')
            )
        )
    )

Infact you don't need outer some

data.filter(item =>  
     item.releases.some(
       obj=>obj.owner.toLowerCase().includes('john')
    )
)

You can try this:

 let data = [ { title: 'aa', releases: [ { version: '1', owner: 'john'}, { version: '2', owner: 'bob'} ] }, { title: 'bb', releases: [ { version: '1', owner: 'john'}, { version: '2', owner: 'jack'} ] }, { title: 'cc', releases: [ { version: '1', owner: 'doo'}, { version: '2', owner: 'park'} ] }, ]; var result = data.filter(x => x.releases.some(rls => rls.owner.toLowerCase().includes(['john']))); console.log(result); 

If the intent is to check for a single name you can negate this answer. However if the intent of the question is to check for multiple names you can fix your code in the following way.

The issue is that you're providing a true / false value to the first some call, this should be a function.

const names = ['john'];
data.filter(item =>
  names.some(name => // added `name =>`
    item.releases.some(release =>
      release.owner.toLowerCase().includes(name) // changed `['john']` into `name`
    )
  )
);

However, you can also achieve the same functionality with less iterations by using an regex.

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
const escapeRegExp = string => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

const names = ['john'],
      pattern = names.map(escapeRegExp).join("|"),
      regex = new RegExp(pattern, "i"); // i = ignore case (removing the need of toLowerCase)

data.filter(item => item.releases.some(release => release.owner.match(regex)));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM