Javascript

[Javascript] Array.from(), Array.prototype.reduce() (feat. 프로그래머스 다리를 지나는 트럭)

binaryJournalist 2021. 4. 21. 13:29
반응형

 

 

 

 

프로그래머스에서 <다리를 지나는 트럭> 풀다 막힌 문제인데

 

 

내 첫 식은 이랬다.

 

function solution(bridge_length, weight, truck_weights) {
    let seconds = 0;
    let onBridge = [ ...truck_weights ];
    do {
        let sumWeight = onBridge.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
        if (sumWeight + waitings[0] <= weight) {
            onBridge.push(waitings[0]);
            waitings.shift();
        } else {
            offBridge.push(onBridge[0]);
            onBridge.shift();
            onBridge.push(waitings[0]);
            waitings.shift();
        }
        seconds++;
        // console.log("waitings");
        // console.log(waitings);
        // console.log("onBridge");
        // console.log(onBridge);
        // console.log("offBridge");
        // console.log(offBridge);
        // console.log(answer);
    }
    while (onBridge.length)
    return seconds + bridge_length;
}

 

 

waitings 가 빈 배열로 남은 상태에도 push가 계속 일어나서 onBride에 계속 undefined 가 들어갔다

그리고 spread 연산자 쓰면서 시간을 꽤 많이 잡아먹게 되었다. 생각해 보니 함수 내 이용을 위해 truck_weights 변수에 선언되어 담기는 건데 또 다시 카피할 필요는 없다고 생각하여 뺐다.

 

 

결국 구글링해서 해답을 찾고 이렇게 바꾸었다.

 

function solution(bridge_length, weight, truck_weights) {
    let seconds = 0;
    let onBridge = Array.from({ length: bridge_length }, () => 0);
    do {
        onBridge.shift();
        seconds++;
        if (truck_weights.length) {
            let sumWeight = onBridge.reduce((accumulator, currentValue) => accumulator + currentValue, 0);             if (sumWeight + truck_weights[0] > weight) {
                onBridge.push(0);
            } else {
                onBridge.push(truck_weights.shift());
            }
        }
    }
    while (onBridge.length)
    return seconds;
}

 

아직까지도 reduce 함수 사용은 어렵다.

 

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

 

Array.prototype.reduce() - JavaScript | MDN

Array.prototype.reduce() The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value. The reducer function takes four arguments: Accumulator Current Value Current Index Source Array Your

developer.mozilla.org

 

모질라에 따르면 reducer 함수는 네 개의 파라미터를 받을 수 있다고 한다.

accumulator, currentValue, currentIndex, originalArray

 

accumulator 는 콜백의 반환값의 누적으로 첫번째 호출은 initialValue을 제공한 경우 initialValue 를 반환한다.

내 경우 initialValue는 0이다,

 

onBridge.reduce((accumulator, currentValue) => accumulator + currentValue, 0);

 

콜백의 최초 호출 때 accumulator와 currentValue는 다음 두 가지 값 중 하나를 가질 수 있습니다. 만약 reduce() 함수 호출에서 initialValue를 제공한 경우, accumulator는 initialValue와 같고 currentValue는 배열의 첫 번째 값과 같습니다. initialValue를 제공하지 않았다면, accumulator는 배열의 첫 번째 값과 같고 currentValue는 두 번째와 같습니다.

참고: initialValue를 제공하지 않으면, reduce()는 인덱스 1부터 시작해 콜백 함수를 실행하고 첫 번째 인덱스는 건너 뜁니다. initialValue를 제공하면 인덱스 0에서 시작합니다.

 

 

object 로 이뤄진 배열의 합의 경우 예제는 이렇다.

 

var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(
    (accumulator, currentValue) => accumulator + currentValue.x
    ,initialValue
);

console.log(sum) // logs 6

 

이건 나중에 사용해봐야겠다.

 

reduce를 이용한다면 괜한 변수 먼저 선언해서 for 문 돌려 더해줄 필요도 없지 않은가...!

 

 

 

 

 

----- 2021-04-22 추가로 좋은 방법을 또 익혀서 추가한다.

언제 사용할지는 모르겠지만

 

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
  function(accumulator, currentValue) {
    return accumulator.concat(currentValue);
  },
  []
);
// 펼친 결과: [0, 1, 2, 3, 4, 5]

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
  ( accumulator, currentValue ) => accumulator.concat(currentValue),
  []
);

 

이렇게 중첩배열을 하나의 배열로 만들어줄 수 있다고 한다.

 

(모든 예제는 모질라 출처이다.)

 

 

 

 

객체 내 인스턴스 세기

 

var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) {
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

 

 

 

 

 

속성으로 객체 분류하기 예제 (이거는 플젝에 썼었다!)

 

var people = [
  { name: 'Alice', age: 21 },
  { name: 'Max', age: 20 },
  { name: 'Jane', age: 20 }
];

function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// {
//   20: [
//     { name: 'Max', age: 20 },
//     { name: 'Jane', age: 20 }
//   ],
//   21: [{ name: 'Alice', age: 21 }]
// }

 

 

 

 

내가 쓴 방식은 이럼 (예제의 활용)

 

 

var people = [
  { name: 'Alice', age: 21, gender: "female" },
  { name: 'Max', age: 20, gender: "male" },
  { name: 'Jane', age: 20, gender: "female" }
];

var genderGroup = people.reduce((rv, { gender, ...rest }) => {
    const key = `${gender}`;
    rv[key] = rv[key] || [];
    rv[key].push(rest);
    rv[key].sort((a, b) => a["age"] - b["age"]);
    return rv;
}, {})

 

 

 

 

 

from도 새로 발견하게 되었다,

 

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

 

Array.from() - JavaScript | MDN

Array.from() The Array.from() static method creates a new, shallow-copied Array instance from an array-like or iterable object. Array.from(arrayLike, (currentValue) => { ... } ) Array.from(arrayLike, (currentValue, index) => { ... } ) Array.from(arrayLike,

developer.mozilla.org

 

모질라에 따르면 얕게 복사한 새 배열을 만들어준다고 한다. 

 

// Generate a sequence of numbers
// Since the array is initialized with `undefined` on each position,
// the value of `v` below will be `undefined`
Array.from({length: 5}, (v, i) => i);
// [0, 1, 2, 3, 4]

 

파라미터로 들어갈 배열이 없으므로 사용할 value 도 없으니 v 는 undefined으로 나올 것이고 return 값은 인덱스로 나오게 한다는 말이다.

 

Array.from({ length: bridge_length }, () => 0);

 

내 코드는 이걸 응용한 건데 위 코드는 애초에 parameter로는 아무것도 받지 않고 bride_length 개수만 맞춰 0으로 구성된 배열을 만든 것이다.

 

 

 

참고로 Array.from()을 이용하면  문자열을 문자마다 쪼개 배열을 만드는 것도 split("") 없이 가능하다.

 

 

var foo = Array.from("foo");

  이것과

var foo = "foo".split("")

 

이것의 결과값은 결국 같다.

 

그래서 Array.from()은 array-like 의 값을 받는다고 했나보다.

 

 

위 모질라에서 나온 Array.from() 을 이용하여 수열 배열 만드는 법...

 

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
range(0, 4, 1);
// [0, 1, 2, 3, 4]

// Generate numbers range 1..10 with step of 2
range(1, 10, 2);
// [1, 3, 5, 7, 9]

 

 

이것은 같은 range 함수로 알파멧 숫자..!

// Generate the alphabet using Array.from making use of it being ordered as a sequence
range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

 

 

오늘도 열공열공...!

반응형