2018년 8월 26일 일요일

Javascript 배열의 모든것 2 ( filter, some, every, reduce )


map 은 배열의 모든것 1탄 에서 다루었으니, 다른 것들도 다뤄보도록 한다.

ㆍfilter

말 그대로 배열에서 어떤 값을 필터링 하고 싶을 때 사용한다.

filter 내부 에서 리턴되는 값이 true 이면 해당 원소를 포함하고,
false 이면 포함하지 않는다.

const data = [1, 2, 3, 4, 5]
// 들어온 val 가 짝수인 경우에 true.
// true 이면 새로운 배열에 포함하고, false 이면 제외한다.
// 짝수를 얻어와 보자.
const evenNumber = data.filter(val => (val % 2 == 0 ) );


ㆍEvery, Some 

배열 값들 모두(Every) 그러한가, 몇개(Some)만 그러한가.

당연히 도중에 답을 찾으면 순회를 그만둔다.

const data = [1, 2, 3, 4, 5];
const isAllEven = data.every(val => { return val % 2 == 0 });
console.log(isAllEven); // false
const isSomeEven = data.some(val => { return val % 2 == 0 });
console.log(isSomeEven); // true

ㆍReduce

사실 map 과 filter 를 이용하면 거의 모든 작업을 다 할 수 있을 것 같은 생각이 든다.

하지만 다음과 같이 배열에서 특정 단어의 개수를 세서
새로운 Object 를 만들고 싶을 때 등 복잡한 작업에는 reduce 가 딱이다.

movie = ["spy","oabe","oabe","ray","spy","room","room","once","ray","spy","once"];
var result = movie.reduce( (obj, value, index, array)=> {
    if(obj.hasOwnProperty(value)) {
        obj[value] += 1;
    } else {
        obj[value] = 1;
    }
    return obj;
}, {});
{ spy : 3,
oabe : 2,
ray : 2,
room : 2,
once : 2 }


만약에 관리를 jsonArray 로 하고싶다면, 다음과 같은 방법을 사용해도 된다.

var movie = ["spy","oabe","oabe","ray","spy","room","room","once","ray","spy","once"];
var idxHash = {};
var result = movie.reduce( (arr, value, index, array)=> {
if(idxHash.hasOwnProperty(value)) {
arr[idxHash[value]].count += 1;
} else {
idxHash[value] = arr.length;
arr.push({title:value, count:1});
}
return arr;
}, []);

[ {title: spy , count: 3}
,{title: oabe, count: 2}
,{title: ray , count: 2}
,{title: room, count: 2}
,{title: once, count: 2}]

눈치 챘겠지만 최초 초기화 타입은 json, array 등 다양하게 줄 수 있고,

초기화 값도 줄 수 있다.

결국 배열을 순회하면서 여기에 값을 채워나가는 형태다.



Javascript 배열의 모든 것 1탄 ( 선언, 정렬, 2차원배열, 멀티 소팅, 순열, 조합 )


ㆍ배열의 선언

// 5개 짜리 배열 선언
var origin = new Array (5);

// 5개 짜리 배열을 선언 할 때 0으로 초기화 하기
var origin =  Array.apply(null, new Array(5)).map(Number.prototype.valueOf,0);

// 5개 짜리 배열을 선언 할 때 "securekim" 으로 초기화 하기
var origin = Array.apply(null, new Array(5)).map(String.prototype.valueOf,"securekim");

// 배열의 복사 (Deep copy)
// for 문 돌면서 직접 대입하는것이 가장 빠르지만, 이것이 그 다음으로 빠르다. 코드가 간결하다는 장점이 있다.
var copy = origin.slice(0);
//참고로 map 이 가장 느리다. (https://jsperf.com/cloning-arrays/3)
var copy = origin.map(x=>(x));

// 배열의 간단 정렬 - 내부 알고리즘은 Merge sort. 브라우저별로 다를 수 있음.
origin.sort(function(a, b) {
    return a - b;
});


ㆍ2차원 배열

2차원 배열이라는게 따로 있는건 아니고 배열 안에 배열을 선언하는 것.

// x,y (4,2) 짜리 배열 선언
// ? ? ? ?
// ? ? ? ?
var origin  = new Array( new Array(4), new Array(4) );

// 이런 이상한 모양의 배열도 만들 수 있음.
// ? ? ? ?
// ? ?
// ? ? ? ?
var origin  = new Array( new Array(4), new Array(2), new Array(4) );

// 배열의 복사 (Deep copy)
// 방법 1.
var copy = new Array(origin.length);
for(var i in origin){
    copy[i] = origin[i].slice(0);
}
// 방법 2.
var copy = origin.map(x => x.map( y=>(y) ));

// 배열 복사(Deep copy) 하면서 0으로 초기화 하기
// 방법 1.
var copy = new Array(origin.length);
for(var i in origin){
 copy[i] = Array.apply(null, new Array(origin[i].length)).map(Number.prototype.valueOf,0);
}
// 방법 2.
var copy = origin.map(x => x.map( y=>(0) ));

ㆍ배열의 정렬 (멀티 소팅)
툰 아저씨가  만들어 놓은 것을 사용하자.

https://github.com/Teun/thenBy.js/blob/master/thenBy.js

/***
Copyright 2013 Teun Duynstee
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var firstBy = (function() {
function identity(v){return v;}
function ignoreCase(v){return typeof(v)==="string" ? v.toLowerCase() : v;}
function makeCompareFunction(f, opt){
opt = typeof(opt)==="number" ? {direction:opt} : opt||{};
if(typeof(f)!="function"){
var prop = f;
// make unary function
f = function(v1){return !!v1[prop] ? v1[prop] : "";}
}
if(f.length === 1) {
// f is a unary function mapping a single item to its sort score
var uf = f;
var preprocess = opt.ignoreCase?ignoreCase:identity;
var cmp = opt.cmp || function(v1,v2) {return v1 < v2 ? -1 : v1 > v2 ? 1 : 0;}
f = function(v1,v2) {return cmp(preprocess(uf(v1)), preprocess(uf(v2)));}
}
if(opt.direction === -1) return function(v1,v2){return -f(v1,v2)};
return f;
}
/* adds a secondary compare function to the target function (`this` context)
which is applied in case the first one returns 0 (equal)
returns a new compare function, which has a `thenBy` method as well */
function tb(func, opt) {
/* should get value false for the first call. This can be done by calling the
exported function, or the firstBy property on it (for es6 module compatibility)
*/
var x = (typeof(this) == "function" && !this.firstBy) ? this : false;
var y = makeCompareFunction(func, opt);
var f = x ? function(a, b) {
return x(a,b) || y(a,b);
}
: y;
f.thenBy = tb;
return f;
}
tb.firstBy = tb;
return tb;
})();

// 이름은 사전순
// 이름이 같으면 키는 오름차순
// 키도 같으면 몸무게는 내림차순으로 정렬

var people = [
{weight:70, height:170, name:"BRABO" },
{weight:70, height:170, name:"CHARLIE" },
{weight:70, height:170, name:"ALPHA" },
{weight:70, height:175, name:"BRABO" },
{weight:70, height:175, name:"CHARLIE" },
{weight:70, height:175, name:"ALPHA" },
{weight:75, height:170, name:"BRABO" },
{weight:75, height:170, name:"CHARLIE" },
{weight:75, height:170, name:"ALPHA" },
{weight:90, height:170, name:"BRABO" },
{weight:75, height:190, name:"CHARLIE" },
{weight:75, height:190, name:"ALPHA" },
]

people.sort(
    firstBy(function (v1, v2) { return v1.name < v2.name ? -1 : v1.name > v2.name ? 1: 0; })
    .thenBy(function (v1, v2) { return v1.height- v2.height; })
    .thenBy(function (v1, v2) { return v2.weight - v1.weight; })
);
// 2차원 배열인 경우.
people.sort(
    firstBy(function (v1, v2) { return v1[2] < v2[2] ? -1 : v1[2] > v2[2] ? 1: 0; })
    .thenBy(function (v1, v2) { return v1[1] - v2[1]; })
    .thenBy(function (v1, v2) { return v2[0] - v1[0]; })
);


ㆍ배열의 순열과 조합

function combi(chars) {
var result = [];
var f = (prefix, chars) => {
for (var i = 0; i < chars.length; i++) {
result.push(prefix + chars[i]);
f(prefix + chars[i], chars.slice(i + 1));
}
}
f('', chars);
return result;
}


function permute(permutation) {
var length = permutation.length,
result = [permutation.slice()],
c = new Array(length).fill(0),
i = 1, k, p;
while (i < length) {
if (c[i] < i) {
k = i % 2 && c[i];
p = permutation[i];
permutation[i] = permutation[k];
permutation[k] = p;
++c[i];
i = 1;
result.push(permutation.slice());
} else {
c[i] = 0;
++i;
}
}
return result;
}