【譯】使用 JavaScript 建立圖

call_me_R發表於2019-05-14

banner

圖是由具有邊的節點集合組成的資料結構。圖可以是有向的或者是無向的。

有向圖包含功能類似於單行道的邊。邊緣從一個節點流向另一個節點。

比如,你可能有一個(關於)人物和電影的圖表,其中每個人可以有多個喜歡的電影,但是電影沒有喜歡的人。

directed_graph

無向圖包含雙向流動的邊緣,類似於雙向道路,兩個方向都有交通。

比如,你可能有一個寵物圖表,其中每隻寵物都有一個所有者,每個所有者都有一隻寵物。

備註:(下面)雙向箭頭代表一條邊,但是為了顯而易見,我繪製了兩條箭頭。

undirected_graph

**圖(graph)**中沒有明確的資訊層次結構。

方法

我們將建立一個(關於)人和冰淇凌口味的圖表。這將是一個有向圖,因為人們可以喜歡某些口味,但是味道可不喜歡人。

我們將建立三個類:

  • PersonNode
  • IceCreamFlavorNode
  • Graph

PersonNode

PersonNode類將接受一個引數:一個人的名字。這將作為其識別符號。

PersonNode建構函式將包含兩個屬性:

  • name:唯一識別符號
  • favoriteFlavors:關於IceCreamFlavorNodes的陣列

另外,PersonNode類包含一個方法:addFlavor。這將傳入一個引數,一個IceCreamFlavorNode物件,並將其新增到陣列favoriteFlavors中。

類的定義如下所示:

class PersonNode {
  constructor(name) {
    this.name = name;
    this.favoriteFlavors = [];
  }

  addFlavor(flavor) {
    this.favoriteFlavors.push(flavor);
  }
}
複製程式碼

IceCreamFlavorNode

IceCreamFlavorNode類將傳入一個引數:冰淇凌口味。這將作為其識別符號。

這個類不需要包含任何方法,因為這是一個無向圖,資料是從person流向flavors,但是不會迴流。

這個類的定義如下:

class IceCreamFlavorNode {
 constructor(flavor) {
   this.flavor = flavor;
 }
}
複製程式碼

Graph

Graph類不接受任何引數,但是其建構函式將包含三個屬性:

  • peopleNodes:人物節點陣列。
  • iceCreamFlavorNodes:冰淇凌口味節點陣列。
  • edges:包含PersonNodesIceCreamFlavorNodes之間的邊緣陣列。

Graph類將包含六個方法:

  • addPersonNode(name):接受一個引數,一個人的名字,建立一個具有此名字的PersonNode物件,並將其推送到peopleNodes陣列。
  • addIceCreamFlavorNode(flavor):接受一個引數,一個冰淇凌口味,建立一個具有這種口味的IceCreamFlavorNode物件,並將其推送到iceCreamFlavorNodes陣列中。
  • getPerson(name):接受一個引數,一個人名字,並返回該人的節點。
  • getFlavor(flavor):接受一個引數,一個冰淇凌口味,並返回該口味的節點。
  • addEdge(personName, flavorName):接受兩個引數,一個人的名稱和一個冰淇凌口味,檢索兩個節點,將flavor新增到人的favoriteFlavors陣列,並將邊推送到edge陣列。
  • print():簡單列印出peopleNodes陣列中的每個人,以及他們最喜歡的冰淇凌口味。

類的定義如下所示:

class Graph {
  constructor() {
    this.peopleNodes = [];
    this.iceCreamFlavorNodes = [];
    this.edges = [];
  }

  addPersonNode(name) {
    this.peopleNodes.push(new PersonNode(name));
  }

  addIceCreamFlavorNode(flavor) {
    this.iceCreamFlavorNodes.push(new IceCreamFlavorNode(flavor));
  }

  getPerson(name) {
    return this.peopleNodes.find(person => person.name === name);
  }

  getFlavor(flavor) {
    return this.iceCreamFlavorNodes.find(flavor => flavor === flavor);
  }

  addEdge(personName, flavorName) {
    const person = this.getPerson(personName);
    const flavor = this.getFlavor(flavorName);
    person.addFlavor(flavor);
    this.edges.push(`${personName} - ${flavorName}`);
  }

  print() {
    return this.peopleNodes.map(({ name, favoriteFlavors }) => {
      return `${name} => ${favoriteFlavors.map(flavor => `${flavor.flavor},`).join(' ')}`;
    }).join('\n')
  }
}
複製程式碼

虛擬資料

現在,我們有了三個類,我們可以新增一些資料並測試它們:

const graph = new Graph(true);
graph.addPersonNode('Emma');
graph.addPersonNode('Kai');
graph.addPersonNode('Sarah');
graph.addPersonNode('Maranda');
graph.addIceCreamFlavorNode('Chocolate Chip');
graph.addIceCreamFlavorNode('Strawberry');
graph.addIceCreamFlavorNode('Cookie Dough');
graph.addIceCreamFlavorNode('Vanilla');
graph.addIceCreamFlavorNode('Pistachio');

graph.addEdge('Emma', 'Chocolate Chip');
graph.addEdge('Emma', 'Cookie Dough');
graph.addEdge('Emma', 'Vanilla');
graph.addEdge('Kai', 'Vanilla');
graph.addEdge('Kai', 'Strawberry');
graph.addEdge('Kai', 'Cookie Dough');
graph.addEdge('Kai', 'Chocolate Chip');
graph.addEdge('Kai', 'Pistachio');
graph.addEdge('Maranda', 'Vanilla');
graph.addEdge('Maranda', 'Cookie Dough');
graph.addEdge('Sarah', 'Strawberry');

console.log(graph.print());
複製程式碼

下面是我們有向圖看起來類似(的樣子):

demo

如果你想看完整的程式碼,到我的CodePen上檢視。

後話

原文:dev.to/emmawedekin…

文章首發:github.com/reng99/blog…

更多內容:github.com/reng99/blog…

相關文章