在具有不同值的对象数组中查找对象的最有效方法是什么?

What would be the most efficient way to find an object in an array of objects with distinct value which is an array?

我最初在几个月前的一次采访中遇到了这个问题,现在已经解决了。

所以我们有这个对象数组,目标是找到一个对象,其中的演员没有在电影中出现超过一次。所以基本上找一部有独特演员的电影。

[
  {
    name: 'The Dark Knight',
    rating: 'PG-13',
    year: 2012,
    bestScene: {
      name: 'fight',
      location: 'sewer',
      sceneLength: 10,
      actors: ['Christian Bale', 'Tom Hardy']
    }
  },
  {
    name: 'Good Burger',
    rating: 'PG',
    year: 1994,
    bestScene: {
      name: 'jump',
      location: 'giant milkshake',
      sceneLength: 5,
      actors: ['Kenan Thompson', 'Kel Mitchell']
    }
  },
  {
    name: 'Sharknado 2: The Second One',
    rating: 'TV-14',
    year: 2013
  },
  {
    name: 'The Big Short',
    rating: 'R',
    year: 2015,
    bestScene: {
      name: 'explanation',
      location: 'casino',
      sceneLength: 20,
      actors: ['Christian Bale', 'Steve Carrell']
    }
  }
]

我为自己设定的目标是使用函数式方法解决它,因此我们自然需要清除没有 bestScene 的对象,如下所示:

const moviesWithActorsPresent = movies.filter((movie) => movie.bestScene)

然后我可以使用 reduce 构造一个对象数组,如下所示:

[
  { 'The Dark Knight': [ 'Christian Bale', 'Tom Hardy' ] },
  { 'Good Burger': [ 'Kenan Thompson', 'Kel Mitchell' ] },
  { 'The Big Short': [ 'Christian Bale', 'Steve Carrell' ] }
]

然后使用 forforEach 循环并在临时变量中跟踪演员,但对我来说这并不是一个完美的解决方案。

我们可以在这里使用什么CS概念来有效地解决它?

获得 moviesWithActorsPresent 后,创建一个对象(或地图)来计算整个数组中每个演员的出现次数。然后你可以 .find 一个对象,其中 .every actor 的计数恰好为 1:

const movies =[
  {
    name: 'The Dark Knight',
    rating: 'PG-13',
    year: 2012,
    bestScene: {
      name: 'fight',
      location: 'sewer',
      sceneLength: 10,
      actors: ['Christian Bale', 'Tom Hardy']
    }
  },
  {
    name: 'Good Burger',
    rating: 'PG',
    year: 1994,
    bestScene: {
      name: 'jump',
      location: 'giant milkshake',
      sceneLength: 5,
      actors: ['Kenan Thompson', 'Kel Mitchell']
    }
  },
  {
    name: 'Sharknado 2: The Second One',
    rating: 'TV-14',
    year: 2013
  },
  {
    name: 'The Big Short',
    rating: 'R',
    year: 2015,
    bestScene: {
      name: 'explanation',
      location: 'casino',
      sceneLength: 20,
      actors: ['Christian Bale', 'Steve Carrell']
    }
  }
];
const moviesWithActorsPresent = movies.filter((movie) => movie.bestScene)
const actorCounts = moviesWithActorsPresent.reduce((a, { bestScene }) => {
  const { actors } = bestScene;
  return Object.assign(
    {}, // don't mutate
    a, // prior counts
    ...actors.map(actor => ({ [actor]: (a[actor] || 0) + 1 }))
  );
}, {});
const movieWithUniqueActors = moviesWithActorsPresent.find(({ bestScene }) => (
  bestScene.actors.every(actor => actorCounts[actor] === 1)
));
console.log(movieWithUniqueActors);

几乎肯定没关系,但如果需要,您可以将 .filter 功能放入 .reduce

const movies = [{
    name: 'The Dark Knight',
    rating: 'PG-13',
    year: 2012,
    bestScene: {
      name: 'fight',
      location: 'sewer',
      sceneLength: 10,
      actors: ['Christian Bale', 'Tom Hardy']
    }
  },
  {
    name: 'Good Burger',
    rating: 'PG',
    year: 1994,
    bestScene: {
      name: 'jump',
      location: 'giant milkshake',
      sceneLength: 5,
      actors: ['Kenan Thompson', 'Kel Mitchell']
    }
  },
  {
    name: 'Sharknado 2: The Second One',
    rating: 'TV-14',
    year: 2013
  },
  {
    name: 'The Big Short',
    rating: 'R',
    year: 2015,
    bestScene: {
      name: 'explanation',
      location: 'casino',
      sceneLength: 20,
      actors: ['Christian Bale', 'Steve Carrell']
    }
  }
];
const actorCounts = movies.reduce((a, { bestScene }) => {
  if (!bestScene) {
    return a;
  }
  const { actors } = bestScene;
  return Object.assign({}, // don't mutate
    a, // prior counts
    ...actors.map(actor => ({
      [actor]: (a[actor] || 0) + 1
    }))
  );
}, {});
const movieWithUniqueActors = movies.find(({ bestScene }) => (
  bestScene.actors.every(actor => actorCounts[actor] === 1)
));
console.log(movieWithUniqueActors);

您只需要创建一个将 films by actors 分组的函数,然后只获取那些具有 1 胶片的函数。

const group = (data) => data
  .reduce((res, { name, bestScene }) => {
    ((bestScene || {}).actors || []).forEach(actor => {
      
      res[actor] = (res[actor] || []).concat(name);
    });
    
    return res;
  }, {});

const solve = data => Object
  .entries(group(data))
  .filter(([author, films]) => films.length === 1)

const data = [
  {
    name: 'The Dark Knight',
    rating: 'PG-13',
    year: 2012,
    bestScene: {
      name: 'fight',
      location: 'sewer',
      sceneLength: 10,
      actors: ['Christian Bale', 'Tom Hardy']
    }
  },
  {
    name: 'Good Burger',
    rating: 'PG',
    year: 1994,
    bestScene: {
      name: 'jump',
      location: 'giant milkshake',
      sceneLength: 5,
      actors: ['Kenan Thompson', 'Kel Mitchell']
    }
  },
  {
    name: 'Sharknado 2: The Second One',
    rating: 'TV-14',
    year: 2013
  },
  {
    name: 'The Big Short',
    rating: 'R',
    year: 2015,
    bestScene: {
      name: 'explanation',
      location: 'casino',
      sceneLength: 20,
      actors: ['Christian Bale', 'Steve Carrell']
    }
  }
];

console.log(solve(data));