【问题标题】:How can I find all Items that meet a certain condition in JSON Data?如何在 JSON 数据中找到满足特定条件的所有项目?
【发布时间】:2019-10-29 07:39:39
【问题描述】:

我从 API 调用中获取数据。我有一个多对多的关系 - 我将与人们解释它的电影。一部电影有很多人看,一个人可以看很多部电影。 所以在 Angular 前端,当你点击一个人时,你应该得到这个人的更详细的视图,包括他看过的电影列表。

当我运行这段代码时(person-detail.component.ts):

  public selectedperson: any = []
/*Array that contains data from the selected person*/
  public movie: any = []
/*Array that contains all the movie-person connections (movie-name and person_id)*/
  public selectedmovie: any = []
/*array that should contain all the movies the selectedperson has watched*/
/*some code*/
getMovies(){
    const url ='http://localhost:4000/api/people-movies';
    this.http.get(url).subscribe(movie => {
      this.movie = movie;
      this.selectedmovie=this.movie.find(item=>
        {
          for (var i = 0; i < this.movie.arrayLength; i++) {
            if(item['person_id']===this.selectedperson.person_id)
            {
              return true;
            }
          return false;
        }
        });
      console.log(this.selectedmovie, this.selectedperson.person_id);
      return this.selectedmovie;
    });
  }

它不起作用 - 但没有这个循环(如果我离开 If 语句):

 for (var i = 0; i < this.movie.arrayLength; i++) {
}

它只返回此人的一部电影 - 因此它一找到电影就停止。但是因为我想列出所有电影,所以我需要循环。

那么我在循环中做错了什么?

编辑: selectedperson 中的示例数据(已填写): 选定的人:

[{"person_id"=34, "name"="john"}]

movie(使用提到的 URL 从 API 调用中获取此数据):

[{"person_id"=33, "movie"="Titanic"}{"person_id"=33, "movie"="Star Wars"}{"person_id"=34, "movie"="Titanic"}{"person_id"=34, "movie"="Star Wars"}{"person_id"=34, "movie"="Indiana Jones"}{"person_id"=35, "movie"="Titanic"}]

想要的最终结果(对于 id 为 34 的人)

Titanic
Star Wars
Indiana Jones

【问题讨论】:

  • 我真的不明白你在做什么。您正在尝试填充 selectedmovie- 这是一个空数组- 通过激活自身的 find 函数...
  • 你能写下你的样本数据和想要的结果吗? selectedmoviemovieselectedperson 的数组?
  • 另外,你正在创建一个for 循环,但不要在内部的任何位置使用i...那有什么意义呢?
  • 如果您想获取所有符合特定条件的元素,请使用过滤器。 find 只返回第一个匹配项
  • @butterfly 基本上,在for 循环中,您执行此item['person_id']===this.selectedperson.person_id 比较X 次(根据movie 长度),但item 不变,this.selectedperson.person_id 也不变。所以它在同一部电影上一遍又一遍地使用相同的兼容性。

标签: javascript arrays json angular loops


【解决方案1】:

您的代码有一些问题,但我将从我认为适合您的解决方案开始:

getMovies(){
    const url ='http://localhost:4000/api/people-movies';
    this.http.get(url).subscribe(movie => {
      this.movie = movie;
      this.selectedmovie=this.movie.filter(item=> item['person_id'] === this.selectedperson.person_id );
      console.log(this.selectedmovie, this.selectedperson.person_id);
      return this.selectedmovie.map(movie.movie);
    });
  }

现在,您的代码有什么问题:

  1. 您尝试通过在其自身上激活 find 来填充一个空数组 selectedmovie,但它是空的...相反,使用在 movie 数组中找到的电影填充它。

  2. 您正在使用find,简单查看文档 (here) 就会清楚它只返回满足测试功能的第一个元素。相反,使用了filter,它有点相同,但会遍历整个数组并返回所有满足测试的对象。

  3. 您创建了一个 for 循环,但没有在其中使用 i - 这表明您实际上并不需要该循环。

  4. 当整个检查是单个布尔表达式时,不需要返回“true”或“false”,它会内联返回。

  5. 只是一个侧面,你的变量名称真的很难理解。例如,包含电影的数组最好称为“电影”,当您使用数组函数对其进行迭代时,每个元素都可以称为“电影”。对两者使用相同的名称确实令人困惑。还可以尝试使用驼峰式表示法(selectedMovie),这样名称会更清晰。

**注意:我的解决方案只会以您想要的方式从“getMovies”返回数组,因为.map 链接在返回值上。如果您希望selectedmovie 仅填充电影名称作为字符串而不是整个movie-perosn_id 对象,只需将.map 链接到filter,如下所示:

this.selectedmovie=this.movie.filter(item=&gt; item['person_id'] === this.selectedperson.person_id ).map(movie =&gt; movie.movie);

希望对您有所帮助:)

【讨论】:

    【解决方案2】:

    你可以这样试试。

    this.selectedmovie = this.selectedmovie.filter(item => {
      for (var i = 0; i < this.movie.arrayLength; i++) {
         if (item['person_id'] === this.selectedperson.person_id) {
            return true;
         }
       }
       return false;
    });
    

    注意:Javascript array.find 方法返回提供的数组中满足提供的测试功能的第一个元素的值。

    【讨论】:

      【解决方案3】:

      你可以像这样得到你想要的结果:

      const movie = [
         {"person_id":33, "movie":"Titanic"},
         {"person_id":33, "movie":"Star Wars"},
         {"person_id":34, "movie":"Titanic"},
         {"person_id":34, "movie":"Star Wars"},
         {"person_id":34, "movie":"Indiana Jones"},
         {"person_id":35, "movie":"Titanic"}
      ];
      
      let selectedperson = [{"person_id":34, "name":"john"}];
      
      let moviesByPerson = movie.filter(f=>
          selectedperson.some(s=> f['person_id'] == s['person_id']));
      
      console.log(`moviesByPerson: `, moviesByPerson);

      在您的代码中:

      getMovies(){
          const url ='http://localhost:4000/api/people-movies';
          this.http.get(url).subscribe(movie => {
            this.movie = movie;
            this.selectedmovie = movie.filter(f=>
                 selectedperson.some(s=> f['person_id'] == s['person_id']));
      
            console.log(this.selectedmovie, this.selectedperson.person_id);
      
            return this.selectedmovie;
          });
        }
      

      此外,尝试将您的 HTTP 调用转移到服务中,因为分离关注点是一种很好的做法。 As Angular docs says:

      组件不应该直接获取或保存数据,它们当然是 不应故意提供虚假数据。他们应该专注于呈现 数据并将数据访问委托给服务。

      【讨论】:

      • 感谢您的详细回答 - 我向我抛出以下错误消息:Property 'filter' does not exist on type 'Object'.
      • @butterfly 请展示movie 有什么。看起来电影的格式与示例数据中显示的格式不同。
      • 更正了我的输入错误,错误消息消失了 - 您的代码适用于正确的数据格式 - 但变量“selectedmovie”仍然为空
      • @butterfly 你能创建一个stackblitz example吗?
      • @butterfly 尝试使用== 而不是===console.log(this.selectedmovie, this.selectedperson.person_id); 显示什么?
      【解决方案4】:

      一些问题,如前所述,您使用的是find,它将仅返回第一个匹配项(如果存在)。您正在寻找 filter 来过滤与 id 匹配的电影。此外,您正试图从subscribe 返回,但这是行不通的。我也会稍微改变你的做法。 不要使用any,它违背了Type脚本的目的。因此,键入您的数据,例如使用接口,它将对您有所帮助,因为编译器可以告诉您您所做的事情是否不符合您的模型,这使得调试更加容易!

      此外,我会将所有 http-requests 移动到服务中,组件只会订阅该 http-request 的结果。总而言之,我建议如下:

      interface Movie {
        person_id: number;
        movie: string;
      }
      
      interface Person {
        person_id: number;
        name: string;
      }
      

      服务:

      import { map } from "rxjs/operators";
      
      // ...
      
      getFilteredMovies(id: number) {
        const url ='http://localhost:4000/api/people-movies';
        return this.http.get<Movie[]>(url).pipe(
          map((movies: Movie[]) => {
            return movies.filter((m: Movie) => m.person_id === id)
          })
        )
      }
      

      组件:

      movies = <Movie[]>[];
      
      selectedPerson = <Person>{ person_id: 34, name: "john" };
      
      constructor(private myService: MyService) {}
      
      ngOnInit() {
        this.myService
          .getFilteredMovies(this.selectedPerson.person_id)
          .subscribe((data: Movie[]) => {
            this.movies = data;
          });
      }
      

      所以我们已经在服务中处理过滤并且组件只订阅。 如果您在其他希望执行过滤的地方调用此函数,则改为在组件中执行过滤。

      STACKBLITZ demo 使用上面的代码。

      【讨论】:

        【解决方案5】:

        我终于解决了——感谢所有人的帮助,尤其是@Gibor! 以下是面临类似问题的人的代码: 但是 - 要知道这段代码绝不是完美的,必须在许多方面进行改进(例如,使用循环来查找所有电影)。我只是在这里发布它,希望它可以帮助有类似问题的人...... 组件.ts:

        import { Component, OnInit } from '@angular/core';
        import { HttpClient } from '@angular/common/http';
        import { ActivatedRoute} from '@angular/router';
        
        @Component({
          selector: 'app-people-detail',
          templateUrl: './people-detail.component.html',
          styleUrls: ['./people-detail.component.css']
        })
        export class PeopleDetailComponent implements OnInit {
          public data: any = []
          public selectedperson: any = []
          public movies: any = []
          public moviesByPerson: any = []
        
          constructor(
            public http: HttpClient, 
            public route: ActivatedRoute) { }
        
        getPeople()
        {
          const person_id = this.route.snapshot.paramMap.get('person_id')
          const people_url ='http://localhost:4000/api/people'
          const movies_url ='http://localhost:4000/api/person-movie'
          this.http.get(people_url).subscribe
            (data => 
              {
                this.data = data;
                this.selectedperson=this.data.find
                (item=>
                  {
                    if(item['person_id']===person_id)
                    {
                     return true;
                    }
                    return false;
                  }
                )
                this.http.get(movies_url).subscribe
            (movies => 
              {
                this.movies = movies;
                this.moviesByPerson=this.movies.filter(item => item.person_pk === this.selectedperson.person_pk)
              }
            )
            console.log(this.selectedperson, this.moviesByPerson)
                return this.selectedperson, this.moviesByPerson
              }
            )
        }
          ngOnInit()
          {
            this.getPeople()
          }
        }
        

        component.html:

        <div><span>ID: </span>{{selectedperson.person_id}}</div>
        <h1>{{selectedperson.name}}</h1>
        <p>{{selectedperson.age}}</p>
        <p></p>
        <table>
            <thead>
                <tr>
                    <th>Movies</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>{{moviesByPerson[0].movie}}</td>
                </tr>
                <tr>
                    <td>{{moviesByPerson[1].movie}}</td>
                </tr>
                <tr>
                    <td>{{moviesByPerson[2].movie}}</td>
                </tr>
            </tbody>
        </table>
        

        【讨论】:

          猜你喜欢
          • 2013-03-05
          • 1970-01-01
          • 2021-12-20
          • 1970-01-01
          • 2012-01-22
          • 2021-06-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多