質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Webサイト

一つのドメイン上に存在するWebページの集合体をWebサイトと呼びます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

4回答

2323閲覧

短く、簡潔なJavascriptの記述をするためにヒントをください

progblog

総合スコア22

Webサイト

一つのドメイン上に存在するWebページの集合体をWebサイトと呼びます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

5クリップ

投稿2020/06/22 13:38

#短く、簡潔なJavascriptの記述をするためにヒントをください

Javascript初心者です。
OpenWeatherMapを使用した練習サイトを作成しました。

具体的には、

  • 現在の天気予報
  • 翌日の3時間ごとの天気予報
  • 翌々日の3時間ごとの天気予報

をAPIで取得し、作成しました。

結果的に思うようなサイトを作ることはできたのですが、Javascriptが非常に長くなってしまいました。

▽対象サイト
https://webprogblog.xsrv.jp/demo/weather/

##個人的に省略できるんじゃないかと思うところ

翌日の6時、9時、12時、15時、18時の天気予報を取得し、それぞれの天気状態によって条件分岐をしました。

Javascript

1// 特定の時間の天気情報を取得 2const nextWEather_6 = data.list[7].weather[0].main; // 翌日の6時 3const nextWEather_9 = data.list[8].weather[0].main; // 翌日の9時 4const nextWEather_12 = data.list[9].weather[0].main; // 翌日の12時 5const nextWEather_15 = data.list[10].weather[0].main; // 翌日の15時 6const nextWEather_18 = data.list[11].weather[0].main; // 翌日の18時 7 8// 取得した天気によって「天気の画像を」を挿入 9if (nextWEather_6 === "Thunderstorm") { 10 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/clouds.png" alt="雷雨">'; 11} else if (nextWEather_6 === "Drizzle") { 12 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/rains.png" alt="霧雨">'; 13} else if (nextWEather_6 === "Rain") { 14 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/rains.png" alt="雨">'; 15} else if (nextWEather_6 === "Snow") { 16 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/snow.png" alt="雪">'; 17} else if (nextWEather_6 === "Atmosphere") { 18 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/clouds.png" alt="濃霧">'; 19} else if (nextWEather_6 === "Clear") { 20 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/clear.png" alt="晴れ">'; 21} else if (nextWEather_6 === "Clouds") { 22 document.getElementById('nextWEather_6').innerHTML = '<img src="asset/img/clouds.png" alt="曇り">'; 23} else { 24 return false; 25} 26if (nextWEather_9 === "Thunderstorm") { 27 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/clouds.png" alt="雷雨">'; 28} else if (nextWEather_9 === "Drizzle") { 29 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/rains.png" alt="霧雨">'; 30} else if (nextWEather_9 === "Rain") { 31 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/rains.png" alt="雨">'; 32} else if (nextWEather_9 === "Snow") { 33 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/snow.png" alt="雪">'; 34} else if (nextWEather_9 === "Atmosphere") { 35 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/clouds.png" alt="濃霧">'; 36} else if (nextWEather_9 === "Clear") { 37 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/clear.png" alt="晴れ">'; 38} else if (nextWEather_9 === "Clouds") { 39 document.getElementById('nextWEather_9').innerHTML = '<img src="asset/img/clouds.png" alt="曇り">'; 40} else { 41 return false; 42} 43 4445464712時、15時、18時も同様に記述する。 48翌々日も同様に。

ここの部分があまりにも長く、同じような処理を繰り返しているので、もっと簡単にかける気がするのですが、その術がわかりません。

どのように記述すればもっと短く簡潔に書けるかご教授いただけますでしょうか。

繰り返しとのことですので、FOR文とかを使うんじゃないかと思ってはいるのですが、正直どのように手を付けていいかわかりません……

##コード すべて

すみません、文字数OVERで載せることができなかったので、下記を参照ください。

Javascript全文

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

まず、天気の名前が一致していたら、その画像やテキストを挿入するという所ですが、名前、テキスト、画像をまとめたObjectの配列(Array)を作って、for...offorEachで回すという手があります。質問のコードより前の所ですが、次のような感じにかけます。

JavaScript

1/*取得した天気によって 2「天気の名前」「天気の画像を」を挿入*/ 3const weathers = [ 4 {name: "Thunderstorm", text: '雷雨', img: 'clouds.png'}, 5 {name: "Drizzle", text: '霧雨', img: 'rains.png' }, 6 {name: "Rain", text: '雨', img: 'rains.png' }, 7 {name: "Snow", text: '雪', img: 'snow.png' }, 8 {name: "Atmosphere", text: '濃霧', img: 'clouds.png'}, 9 {name: "Clear", text: '晴れ', img: 'clear.png' }, 10 {name: "Clouds", text: '曇り', img: 'clouds.png'}, 11] 12for (const weather of weathers) { 13 if (weatherName == weather.name) { 14 patternTextTarget.innerHTML = weather.text; 15 patternImgTarget.innerHTML = `<img src="asset/img/${weather.img}" alt="${weather.text}">`; 16 break; 17 } 18}

このweathersはトップレベルで書いておけば、その後の似たような挿入部分も同じような処理で書けるようになります。

次に、同じような処理は関数にして、違っている部分だけを関数として渡せば良いです。

JavaScript

1const weatherSet = ({target, time, num}) => { 2 const targetData = data.list[num].weather[0].main; 3 for (const weather of weathers) { 4 if (targetData == weather.name) { 5 document.getElementById(`${target}WEather_${time}`).innerHTML = `<img src="asset/img/${weather.img}" alt="${weather.text}">`; 6 break; 7 } 8 } 9 const targetTemp = Math.round(data.list[num].main.temp); 10 document.getElementById(`${target}Temp_${time}`).innerHTML = targetTemp + '℃'; 11};

渡すときも、配列にまとめてforEachで流し込みましょう。

JavaScript

1const targets = []; 2for (let i = 0; i < 5; i++) { 3 targets.append({target: 'next', time: 6 + 3 * i, num: 7 + i}); 4} 5for (let i = 0; i < 5; i++) { 6 targets.append({target: 'afNext', time: 6 + 3 * i, num: 15 + i}); 7} 8targets.forEach(weatherSet);

上はあくまでイメージで、さらっと書いた物で、ちゃんと動作テストまではしていません。上を単純に組み込むのではなく、理解しながら、自分で書くようにしてください。

このように同じことをしているところは繰り返しでまとめるです。また、法則性のある処理は関数にして細分化していくとコードがすっきりしていくと思います。

投稿2020/06/22 14:25

編集2020/06/22 14:32
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

progblog

2020/06/23 00:56

ご回答ありがとうございました。 ソースだけでなく、適宜コメントをいただき、非常に参考になりました。 教えていただいたことを生かし、ソースをいかに簡潔に書けるか作り直してみようと思います。
guest

0

ベストアンサー

こんにちは

以下のような修正が考えられます。

  • 各天気を表すキー(例:Thunderstorm)に対応する、表示文字列(例:雷雨)と画像ファイル名(例: clouds) をマッピングさせるオブジェクトを定義。これを使用して if文を削除
  • 翌日および翌々日の6,9,12,15,18時の天気情報を持つオブジェクトを要素とする、(長さ10の)配列を作り、これを forEach でループさせる。
  • 画像および気温を表示する<span>の取得を id で行わず、上記のforEach のループインデクスに対応する td.weather-dot-cellを取得して、そこから各<span>を特定する。

上記の修正を行った一例として、以下のような感じになります。

javascript

1// 天気の表示文字列および画像ファイル名 2const WEATHERS = { 3 Thunderstorm: { label: '雷雨', img: 'clouds' }, 4 Drizzle: { label: '霧雨', img: 'rains' }, 5 Rain: { label: '雨', img: 'rains' }, 6 Snow: { label: '雪', img: 'snow' }, 7 Atmosphere: { label: '濃霧', img: 'snow' }, 8 Clear: { label: '晴れ', img: 'clear' }, 9 Clouds: { label: '曇り', img: 'clouds' } 10}; 11 12// 日にちを入れるターゲットを定義する 13const nowTimeTarget = document.getElementById('nowData'); 14const nextTimeTarget = document.getElementById('nextData'); 15const afNextTimeTarget = document.getElementById('afNextData'); 16 17// 現在の日時を取得 18const now = new Date(); 19 20// 取得した日時をターゲットに代入 21nowTimeTarget.innerHTML = (now.getMonth() + 1) + '月' + now.getDate() + '日 ' + now.getHours() + '時' + now.getMinutes() + '分'; 22nextTimeTarget.innerHTML = (now.getDate() + 1); 23afNextTimeTarget.innerHTML = (now.getDate() + 2); 24 25// 位置情報を取得開始 26if (navigator.geolocation) { 27 // 位置情報が取得成功した時 28 navigator.geolocation.getCurrentPosition(function (position) { 29 30 // 緯度経度を取得 31 const basePosition = position.coords; 32 const lat = basePosition.latitude; 33 const lng = basePosition.longitude; 34 35 // WEB API を使用し、現在地の現在の天気を取得 36 const API_KEY = "????????????????????????????????"; 37 const url = "https://api.openweathermap.org/data/2.5/weather" + "?lat=" + lat + "&lon=" + lng + "&units=metric&APPID=" + API_KEY; 38 const request = new XMLHttpRequest(); 39 request.open("GET", url, true); 40 request.responseType = "json"; 41 42 // 現在の天気に関する関数 43 request.onload = function () { 44 const nowData = this.response; 45 46 // 「現在の天気」「現在の気温」「現在地」を取得 47 const weatherName = nowData.weather[0].main; 48 const weatherTemp = nowData.main.temp; 49 const cityName = nowData.name; 50 51 // 取得した現在地が「Suguri」の時の処置 52 if (cityName === 'Suguri') { 53 alert('現在位置をうまく取得することができませんでした。\nしばらくお待ちいただき、再度アクセスいただけますでしょうか?\n「Suguri」の天気を表示させていただきます。どこかは知りません。') 54 } 55 // 取得したもの入れるターゲットを定義 56 const geoTarget = document.getElementById('cityName'); 57 const patternTextTarget = document.getElementById('pattern'); 58 const patternImgTarget = document.getElementById('picture'); 59 const patternTempTarget = document.getElementById('temp'); 60 61 // 現在地、気温をターゲットに代入 62 geoTarget.innerHTML = cityName; 63 patternTempTarget.innerHTML = weatherTemp + '<span class="unit">℃</span>'; 64 65 /*取得した天気によって 66 「天気の名前」「天気の画像を」を挿入*/ 67 const weather = WEATHERS[weatherName]; 68 if (weather) { 69 const { label, img } = weather; 70 patternTextTarget.innerHTML = label; 71 patternImgTarget.innerHTML = `<img src="asset/img/${img}.png" alt="${label}">`; 72 } else { 73 return false; 74 } 75 }; 76 request.send(); 77 78 // WEB API を使用し、現在地の翌日以降の天気を取得 79 const nextUrl = "https://api.openweathermap.org/data/2.5/forecast" + "?lat=" + lat + "&lon=" + lng + "&units=metric&APPID=" + API_KEY; 80 const nextRequest = new XMLHttpRequest(); 81 nextRequest.open("GET", nextUrl, true); 82 nextRequest.responseType = "json"; 83 84 // 現在、翌日以降の天気に関する関数 85 nextRequest.onload = function () { 86 const data = this.response; 87 88 // 翌日および翌々日の6,9,12,15,18時の天気情報を取得し、対応する画像と気温を設定 89 const cells = document.querySelectorAll('.weather-dot-cell'); 90 [...data.list.slice(7, 12), ...data.list.slice(15, 20)].forEach((e, i) => { 91 const weather = WEATHERS[e.weather[0].main]; 92 if (weather) { 93 const { label, img } = weather; 94 cells[i].querySelector('.weather-dot').innerHTML = `<img src="asset/img/${img}.png" alt="${label}">`; 95 cells[i].querySelector('.weather-temp').textContent = `${e.main.temp}`; 96 } 97 }); 98 }; 99 nextRequest.send(); 100 }, 101 102 // 位置情報の取得失敗した時 103 function () { 104 alert('位置情報の取得に失敗しました。'); 105 }); 106} else { 107 alert("位置情報を取得できませんでした……"); 108} 109

上記の修正後の app.js を以下のGithubにも上げておきました。

ご質問にある元の app.jsからの差分を、以下から確認できます。

以上、参考になれば幸いです。

投稿2020/06/22 16:55

編集2020/06/22 23:54
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

progblog

2020/06/23 00:54

ご回答ありがとうございます。 ソース、差分ともに拝見させていただきした。 こんなにも綺麗でシンプルになるのかと驚愕し、勉強不足を痛感しました。 不明点は調べつつ、ソースを参考にしていただきます。 ご丁寧にありがとうございました。
jun68ykt

2020/06/23 01:18

どういたしまして???? 参考になれば幸いです。
progblog

2020/06/23 09:02

とても参考になりました。 連想配列? を使うのですね。 ソースを理解するため、他のものに書き換えて見たのですが1点わからない点がございます。 もしまだ見ていただけるのであれば、ご教授いただけますでしょうか。 ★-- // 本たちを定義 const BOOKS = { AHORESSYA: { autor: '内田百閒', genre: '随筆'}, KUSAMEIKYU: { autor: '泉鏡花', genre: '小説'}, UMI: { autor: '小川洋子', genre: '小説'}, } // 本の名前を定義 const booksName = 'KUSAMEIKYU'; // 本を定義 const book = BOOKS[booksName]; // もし本が「'KUSAMEIKYU'」の時 if(book) { const {autor, genre} = book; // ① ここのソースで行っている処理がわかりません……。 console.log('著者は' + autor + 'でジャンルは' + genre + 'です'); } --★ ① の箇所ですがどのような意味になるのでしょうか? また、まだ自身の手で書いていないのですが、 ★-- [...data.list.slice(7, 12), ...data.list.slice(15, 20)].forEach((e, i) => { --★ 上記ソースの「...data」はなんでドットがこんなにあるのでしょうか? 申し訳ございません。 ご回答いただけますと幸いです。
jun68ykt

2020/06/23 12:04

コメントありがとうございます。 (1) オブジェクトの分割代入 > ① の箇所ですがどのような意味になるのでしょうか? ①の、以下の1行 > const { autor, genre } = book; によって、以下の2行 const autor = book.autor; const genre = book.genre; と同じことをやってくれます。上記の2行を const { autor, genre } = book; という1行で書けるようにする、このような、autorと genre を囲むカッコ { ・・・ } 使いの構文のことを、 分割代入 (Destructuring assignment) といいます。MDN では以下のページ https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment の中の「オブジェクトの分割代入」の節に説明があります。また、このページの前半に説明されている、「配列の分割代入」のほうも上手く使えると、コードをすっきり書くのに役立つことがあります。 (2) スプレッド構文 > ★-- > [...data.list.slice(7, 12), ...data.list.slice(15, 20)].forEach((e, i) => { > --★ > > 上記ソースの「...data」はなんでドットがこんなにあるのでしょうか? 上記のソース行で使われている、ピリオドを3個続けた、... を使った構文を スプレッド (Spread)構文 といいます。MDN では以下のページに説明があります。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax 特に今回使った手法は、上記のページの中の「配列リテラルでのスプレッド構文」の節に書かれています。簡単な例を挙げますと、 const a = [ 1, 2, 3 ]; という配列があったときに、 const b = [ ...a, 4, 5 ]; とすると、b には [ 1, 2, 3, 4, 5 ] という配列が入ります。b の先頭から3つの要素、1, 2, 3 は ...a によって、a の内容が展開されたものです。これをふまえて、ご質問いただいた、回答のコードにある > [...data.list.slice(7, 12), ...data.list.slice(15, 20)].forEach((e, i) => { についてですが、上記の1行をもうちょっと丁寧に、複数行に分けて書くと、以下のようになります。 const nextWeathers = data.list.slice(7, 12); // 翌日の 6,9,12,15,18 時の天気情報を持つ配列 const afNextWeathers = data.list.slice(15, 20); // 翌々日の 6,9,12,15,18 時の天気情報を持つ配列 const weathers = [ ...nextWeathers, ...afNextWeathers ]; // 上記2つの配列を連結した配列 weathers.forEach((e, i) => { ・・・ data.list の中で、翌日の 6,9,12,15,18 時の天気情報はインデクス 7 から 11の部分配列 、翌々日の 6,9,12,15,18 時の天気情報はインデクス 15から 19 の部分配列にあり、インデクス12、13, 14 の要素は不要です。この、インデクス7〜11と、15〜19 にある10個の要素が持つ情報をHTMLに反映させるときに、翌日分と翌々日分とでそれぞれ個別にforEachしてもよいですが、できれば一度のforEachで済ませたいです。そうすると、これらの、離れた区間にある2個の部分配列にある10個の要素を、すきまなく並べた配列が欲しいです。そこで、そのような配列を得るためにはどうするかというと、空の配列を用意して for 文の中で push で詰めていく、というやり方が基本ですが、スプレッド構文を使えば [...data.list.slice(7, 12), ...data.list.slice(15, 20)] で(一気に)得られる、というわけです。 補足ですが、オブジェクトのリテラルでもスプレッド構文を使えます。詳しくは上記のMDNのページの「Objectリテラルで使う」をご参照ください。 以上参考になれば幸いです。
progblog

2020/06/23 13:45

今回もご丁寧にご回答いただき、ありがとうございます。 非常に参考になりました。 正直、力不足で理解できない箇所もありますが、色々と試しつつ練習したいと思います。
guest

0

同じような処理が続くときは配列が使えるかもしれません。

javascript

1const nextWEather_6 = data.list[7].weather[0].main; // 翌日の6時 2 3//nextWEather_6のチェックは済ませておくこと 4 5const t2g = {Thunderstorm:'<img src="asset/img/clouds.png" alt="雷雨">', 6 Drizzle:'<img src="asset/img/rains.png" alt="霧雨">', 7 Rain:'<img src="asset/img/rains.png" alt="雨">', 8 Snow:'<img src="asset/img/snow.png" alt="雪">', 9 Atmosphere:'<img src="asset/img/clouds.png" alt="濃霧">', 10 Clear:'<img src="asset/img/clear.png" alt="晴れ">', 11 Clouds:'<img src="asset/img/clouds.png" alt="曇り">'}; 12 13document.getElementById('nextWEather_6').innerHTML = t2g[nextWEather_6];

もっと削れるとは思いますが、見やすさとか拡張性とかもろもろのバランスをみて調整すればいいかと。

投稿2020/06/22 14:23

takasima20

総合スコア7460

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

まずは、「天候の文字列から、画像のHTMLを生成する」部分を1つの関数にしてしまってはどうでしょうか。

昼用と夜用は必要かもしれませんが、それでもだいぶスッキリするかと思います。

投稿2020/06/22 13:46

maisumakun

総合スコア145208

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問