Sam
Sam

Reputation: 45

How to filter array based on nested value in Javascript

I have an array which I'm trying to filter based on a certain nested value, without getting any other elements.

const arrayData = [ 
{ 
    country: "Country X", 
    games: [ 
        { 
            gameTitle: 'Game1', 
            players: [{ name: 'John', status:'Active' },{ name: 'Rob', status:'Suspended' }] 
        }, 
        { 
            gameTitle: 'Game2', 
            players: [{ name: 'Saly', status:'Blocked' }] 
        }, 
    ] 
}, 
{ 
    country: "Country Y", 
    games: [ 
        { 
            gameTitle: 'Game1', 
            players: [{ name: 'Sindy', status:'Pending' },{ name: 'someone', status:'Rejected' }] 
        }, 
        { 
            gameTitle: 'Game2', 
            players: [{ name: 'Alex', status:'New' },{ name: 'Nic', status:'Old' }] 
        }, 
    ] 
}, 
]; 

What I have tried:

let output = arrayData.filter(eachVal => { 
let opt = eachVal.games.some(( 
    { players }) => players 
    .some(({ status}) => status === 'Active')); 
return opt; 
}) 

Expected result for finding all players with value status : 'Active':

{ 
country: "Country X", 
games: [ 
    { 
        gameTitle: 'Game1', 
        players: [{ name: 'John', status:'Active' }] 
    }
] 
}

But the result:

[{"country":"Country X","games":[{"gameTitle":"Game1","players":[{"name":"John","status":"Active"},{"name":"Rob","status":"Suspended"}]},{"gameTitle":"Game2","players":[{"name":"Saly","status":"Blocked"}]}]}]

Upvotes: 3

Views: 212

Answers (4)

The Bomb Squad
The Bomb Squad

Reputation: 4327

The logic is nested filter through each level and assigning the finds back to their respective places and using forEach logic to see if an object has an "Active" value

const arrayData = [ 
{ 
    country: "Country X", 
    games: [ 
        { 
            gameTitle: 'Game1', 
            players: [{ name: 'John', status:'Active' },{ name: 'Rob', status:'Suspended' }] 
        }, 
        { 
            gameTitle: 'Game2', 
            players: [{ name: 'Saly', status:'Blocked' }] 
        }, 
    ] 
}, 
{ 
    country: "Country Y", 
    games: [ 
        { 
            gameTitle: 'Game1', 
            players: [{ name: 'Sindy', status:'Pending' },{ name: 'someone', status:'Rejected' }] 
        }, 
        { 
            gameTitle: 'Game2', 
            players: [{ name: 'Alex', status:'New' },{ name: 'Nic', status:'Old' }] 
        }, 
    ] 
}, 
];

//answer
let arr=JSON.parse(JSON.stringify(arrayData))
arr=arr.filter(a=>{
  let f=a.games; let i=false
  f=f.filter(b=>{
    let x=b.players; let j=false
    x=x.filter(c=>c.status=="Active")
    x.forEach(c=>{if(c.status=="Active"){j=true}})
    b.players=x; return j
  })
  f.forEach(b=>{
    b.players.forEach(c=>{if(c.status=="Active"){i=true}})
  })
  a.games=f; return i
})
console.log(arr)

Upvotes: 2

Scott Gnile
Scott Gnile

Reputation: 525

Try this:

arrayData.map(({country, games: g}) => {
  
  const games = g.map((g) => { 
    const players = g.players.filter((p) => p.status === 'Active')
  
    return {...g, players}
  })
  .filter(({players}) => players.length > 0)
  
  return {country, games}
}).filter(({games}) => games.length>0)

Upvotes: 3

Amir
Amir

Reputation: 1905

It's quite easy. You just need some map and filter:

const arrayData = [ 
{ 
    country: "Country X", 
    games: [ 
        { 
            gameTitle: 'Game1', 
            players: [{ name: 'John', status:'Active' },{ name: 'Rob', status:'Suspended' }] 
        }, 
        { 
            gameTitle: 'Game2', 
            players: [{ name: 'Saly', status:'Blocked' }] 
        }, 
    ] 
}, 
{ 
    country: "Country Y", 
    games: [ 
        { 
            gameTitle: 'Game1', 
            players: [{ name: 'Sindy', status:'Pending' },{ name: 'someone', status:'Rejected' }] 
        }, 
        { 
            gameTitle: 'Game2', 
            players: [{ name: 'Alex', status:'New' },{ name: 'Nic', status:'Old' }] 
        }, 
    ] 
}, 
];

/*------------------------------- This is the answer -------------------------------*/
let result = arrayData.map(a => ({...a, games: a.games.map(g => ({...g, players: g.players.filter(p => p.status === 'Active')})).filter(g => g.players.length)})).filter(a => a.games.length)
/*----------------------------------------------------------------------------------*/
console.log(result)

Upvotes: 1

Nicoowr
Nicoowr

Reputation: 809

I'd "recursively" select the valid elements at each level and map on each filtered array to build the correct output:

const selectActivePlayers = (players) => players.filter(player => player.status === "Active");
const selectValidGames = (games) => games.filter(game => selectActivePlayers(game.players).length > 0)
const selectValidCountries = (countries) => countries.filter(country => selectValidGames(country.games).length > 0);


const cleanCountries = selectValidCountries(arrayData).map(country => ({
  ...country,
  games: selectValidGames(country.games).map(game => ({
    ...game,
    players: selectActivePlayers(game.players)
  }))
}))

Output:

[
  {
    "country": "Country X",
    "games": [
      {
        "gameTitle": "Game1",
        "players": [
          {
            "name": "John",
            "status": "Active"
          }
        ]
      }
    ]
  }
]

Upvotes: 1

Related Questions