前提・実現したいこと
質問は表題の通り。以下は理解のためのイメージコードです。
※ただし実際の要素はリテラルでなくObjectです。実際のお試しコードは後述。
javascript
1const a = [0,1,2,3]; 2const b = [2,3,4,5]; 3const [onlyA, onlyB] = getUnique(a, b); 4console.log(onlyA) // [0,1] 5console.log(onlyB) // [4,5] 6 7function getUnique(a,b) { 8 // この中身を書きたい 9 return [onlyA, onlyB] // 戻り値も目的が達せられれば変わっても可 10}
お題が定性的ですが、以下の「試したソースコード」のテストコードよりも早ければありがたいな、という感じです。2回for回すのを回避する方法ないのかな、という感じ。
試したソースコード
以下がテストコードです。for文2回で頑張るパターンとfilter2回やるパターンでやりました。のJSFiddle
javascript
1const test = [getUnique1, getUnique2]; 2 3function init() { 4 const a = []; 5 const b = []; 6 const num = 1000; 7 for (let i = 0; i < num; i++) { 8 const x = i; 9 const y = i % 20; 10 if (i >= 100) { 11 a.push({ 12 x, 13 y 14 }) 15 } 16 if (i < 900) { 17 b.push({ 18 x, 19 y 20 }) 21 } 22 } 23 b.sort(() => Math.random() - 0.5); // 適当にbのソートを崩す目的 24 return [a, b] 25} 26 27/** for文で頑張る */ 28function getUnique1(a, b) { 29 const onlyA = []; 30 const onlyB = []; 31 for (let ai = 0, al = a.length; ai < al; ai++) { 32 let hit = false; 33 for (let bi = 0, bl = b.length; bi < bl; bi++) { 34 if (a[ai].x === b[bi].x && a[ai].y === b[bi].y) { 35 hit = true; 36 break; 37 } 38 } 39 if (!hit) { 40 onlyA.push(a[ai]); 41 } 42 } 43 for (let bi = 0, bl = b.length; bi < bl; bi++) { 44 let hit = false; 45 for (let ai = 0, al = a.length; ai < al; ai++) { 46 if (a[ai].x === b[bi].x && a[ai].y === b[bi].y) { 47 hit = true; 48 break; 49 } 50 } 51 if (!hit) { 52 onlyB.push(b[bi]); 53 } 54 } 55 return [onlyA, onlyB]; 56} 57 58/** filter使う */ 59function getUnique2(a, b) { 60 return [ 61 a.filter((av) => b.findIndex((bv) => av.x === bv.x && av.y === bv.y) === -1), 62 b.filter((bv) => a.findIndex((av) => av.x === bv.x && av.y === bv.y) === -1) 63 ] 64} 65 66const result = [] 67const [a, b] = init(); 68for (let i = 0; i < test.length; i++) { 69 const start = Date.now(); 70 // 一度結果のテスト 71 const [onlyA, onlyB] = test[i](a, b); 72 if (onlyA.length !== 100 || onlyB.length !== 100 || onlyA.map(v => v.x).reduce((a, b) => a + b) !== 94950 || onlyB.map(v => v.x).reduce((a, b) => a + b) !== 4950) { 73 alert(`error(expect 100): onlyA.length:${onlyA.length} xSum:${onlyA.map(v => v.x).reduce((a, b) => a + b)}, onlyB.length:${onlyB.length} xSum:${onlyB.map(v => v.x).reduce((a, b) => a + b)}`) 74 console.log(onlyA) 75 console.log(onlyB) 76 return; 77 } 78 for (let j = 0; j < 1000; j++) { 79 test[i](a, b); 80 } 81 const end = Date.now(); 82 result.push({ 83 time: end - start, 84 name: test[i].name 85 }) 86} 87 88const strs = result.map(v => `${v.name}: ${v.time}ms`) 89alert(strs); 90
私の環境でChromeにてfor文2回(getUnique1)が 931ms、filter使うのが(getUnique2) 694msでした。(for文のが早いかなと予想してたがfilterの方が早かった。しかしIEでポリフィルとか使うときっとfilterの方が遅いと思う。)