2017-10-01 2 views
0

knockoutJS를 사용하는 태그 지정 시스템을 사용하는 목록 시스템에서 작업하고 있습니다. 아이디어는 다른 태그와 여러 목록을 한 후 어떤 일치를 표시하는 태그 (0, 1 이상의 태그) 각각 나름대로의효율적으로 모든 일치 항목을 찾는 방법 및 하나의 배열을 다른 배열과 비교하는 일치 없음

-

'항목'의 중심 목록이 있습니다. 예를 들어

: -

item 1 - tags 1,3 
item 2 - tags 2,4 
item 3 - tags 4 
item 4 - tags NONE 

시스템, 항목을 선택하는 '더리스트'(대한 '태그가'하나 하나에 의해 생성 된

List 1 - tags 1,2 
List 2 - tags 3 

플러스 2 추가 목록 목록과 일치하지 않는 태그 및 항목 없음).

에이 초래 : - 나는 각 목록에서 개최 된 항목을 여러 루프를 실행하고 재설정하고있는 순간으로 나는 데 문제가 목록에 항목을 정렬하는 효율적인 방법입니다

List NO TAGS = item 4 
List NO LIST = item 3 
List 1  = item 1, item 2 
List 2  = item 1 

변경 될 때마다

아래의 코드는 내가 아는 추한, 그것은 참조를 위해 더, 나는 질문이 바닥

UGLY WORKING 예

에 표시된 데이터 구조에 태그를 비교하는 가장 효율적인 방법이 무엇인지 가정하자
ko.computed(function() { 
//clear all items in arrays 
ko.utils.arrayForEach(self.lists(), function (list) { 
    list.items([]); 
}); 
var onList; //does this item appear on a list yet 
//loop through all items and then all lists to see if an item matches any tags on that list and if so add it. 
ko.utils.arrayForEach(self.items(), function (item) { 
    onList = 0; //reset whether found - used for adding to 'No List' 
    ko.utils.arrayForEach(self.lists(), function (list) { 


      //if an item has no tasks then add to list id - 2 'No Tags' List 
      if (item.tags().length == 0 && list.id() == -2) { 
       list.items.push(item); 
       onList = 1; //found a list 
      } else { 
       //now for each item loop through tags and then loop through lists tags and find any matches 
       ko.utils.arrayFirst(item.tags(), function (tag) { 
        var found = 0; 

        ko.utils.arrayFirst(list.tags(), function (listTag) { 

         console.log(listTag, tag); 
         if (parseInt(listTag) == parseInt(tag)) { 
          list.items.push(item); 
          found = 1; 
          onList = 1; 
          //at least one tag matches, so exit 
          return 1; 
         } 
        }); 

        if (found == 1) { 
         //at least one tag matches, so exit 
         return 1; 
        } 
       }); 
      } 
     }); 
     //there were no matches in any of the lists so add this to the list 'No List' 
     if(onList == 0 && self.lists().length > 1){ 
       self.lists()[1].items.push(item); 
     } 

    }); 
}); 

데이터 구조

///4 lists - the first 2 are system generated 'catch' lists 
var listData = [ 
    {id: -2, name: "NO TAGS", tags: [ 
      //NOT APPLICABLE AS HANDLED BY CODE 
     ] 
    }, 
    {id: -1, name: "NO LIST", tags: [ 
      //NOT APPLICABLE AS HANDLED BY CODE 
     ] 
    }, 
    {id: 1, name: "List 1", tags: [ 
      2 
     ] 
    }, 
    {id: 2, name: "List 2", tags: [ 
      1, 3 
     ] 
    } 
]; 

//2 sets of items 
var itemData = [ 
    {id: 1, name: "Item 1", addedBy: 1, tags: [ 
      1 
     ]}, 
    {id: 2, name: "Item 2", addedBy: 1, tags: [ 
      2, 3 
     ]} 
]; 

답변

2

나는 명확한 대답을 얻지 못했고 https://codereview.stackexchange.com/에 대한 더 많은 답변을 얻을 수 있을지 궁금해하지만 귀하의 질문에 대해 조금 생각하는 것을 즐겼습니다.

댓글에 약간 깁니다 만 답변이 아닙니다. 어쨌든 유용 할 것입니다.

나는 당신의 접근 방식이 너무 나쁘다고 생각하지 않습니다. 문제를 해결하기위한 긴급한 코드 블럭으로서 명백한 방식으로 일을합니다. list.tags() 및 item.tags()의 교차점 중 하나를 해시로 사전 처리하여 속도를 높일 수 있습니다. 그러나 arrayFirst를 잘 사용하면 일치 항목이 발견되는 즉시 두 루프를 종료 할 수 있습니다.

현대 JS & 브라우저는 시간이 많이 걸리는 대용량 목록을 위해이 문제를 해결할 것입니다. 특히 KO에서이 문제는 브라우저에서 목록을 렌더링 할 때 발생할 수 있습니다. 이미 성능 문제를 발견했거나보다 학문적 관점에서이 문제에 대해 궁금해하십니까?

성능상의 문제가있는 경우 KO가 DOM에서 물건을 만드는 데 상당히 느리기 때문에 1000 개 항목의 목록이 눈에 띄어 렌더링하는 데 1000 개의 항목에 대한 HTML 문자열을 만든 다음 컨테이너의 innerHTML 속성을 사용하여 한 번에 DOM에 저장합니다.하나에 의해 목록 하나에 항목을 추가 할 경우

list.items.push(item); 

self.lists()[1].items.push(item); 

이의 문제는 여기에 유용한 블로그 게시물에서 설명과 코드에서처럼 이것은 특히 사실이다 :

http://www.knockmeout.net/2012/04/knockoutjs-performance-gotcha.html

블로그 게시물, 다음 repopula 그 접근 방식에서, 그래서 한 번에 각 목록을 지 웁니다 코드를 당신의 observableArrays을 설정하는 것을 권장합니다 임시 배열에 일치 항목을 저장하기 위해 약간 다시 작성한 다음 한 번에 관찰 가능 항목에 저장하십시오.

또 다른주의해야 할 것은 4 개의 목록이 있고 위의 경우에도 각 목록이 채워질 때마다 UI를 다시 그리기위한 4 개의 알림이 생성된다는 것입니다. 당신은 연기 업데이트를 이용하여이 문제를 해결할 수 :

http://knockoutjs.com/documentation/deferred-updates.html

귀하의 알고리즘은 다음 목록을 통해 항목을 반복하고 항목이 주어진 목록에 속해 있는지 협력하여 각 목록을 구축하는 접근 방식을 취하고. 목록을 없애지 않는 대안은 항목을 반복 한 다음 목록을 반복하는 것입니다. 목록에서 항목을 추가하거나 제거해야하는 경우 각 항목에 대해 해결합니다. 알고리즘 적으로 여전히 항목과 모든 목록을 반복해야하고 목록 필터와 항목 태그의 교차 부분을 계산해야하므로 상황이 나아질 것이라고 생각하지 않습니다. 그리고 KO의 관점에서 볼 때, 목록에서 항목을 추가하거나 제거 할 때마다 observableArray가 여전히 변경되므로 더 빠를 것이라고 생각하지 않습니다.

KO는 프로그래밍의 기능적 스타일을 촉진하는 경향이 있으며 나는 그 관점에서 볼 때 코드가 추악하다고 주장 할 수 있다고 생각합니다. 그러나 자신이 너무 가혹하다고 생각합니다. 기능 스타일을 사용하면 ko.computed 함수에서 목록 일치를 생성 할 수 있습니다. 무언가가 바뀔 때마다 각 목록을 명시 적으로 비울 필요가 없습니다. 아마도 이것은 KO의 의존성 추적이 DOM을 다시 렌더링하지 않는 관점에서 목록이 변경되지 않는 경우 영리한 작업을 수행 할 수있게 할 것입니다. 이것의 예를 들어,이 바이올린을 썼다 :

https://jsfiddle.net/325gp71a/3/

어떤 일이 수행 : 제가 말씀 드린 그 해시의 deferUpdates의

만들기 사용

// Using deferUpdates=true will help performance when altering the lists. 
// - When true, all lists will be recalculated before UI redrawing 
// - When false, as each list changes the UI will redraw 
// In my browser ,the time taken to add lots of items when clicking the test 
// button is: 
// - deferUpdates=false takes approx 10 seconds 
// - deferUpdated=true takes approx 4 seconds 
// 
ko.options.deferUpdates = true; 

만들기 사용 위의 태그/필터 교차점을 조금 더 빠르게 만들 수 있습니다 :

항목에 대해

:

// Reduce the filter to a hash to remove one loop from the matching process. 
// The filter [2,3] will become the hash {2: true, 3: true} 
this.hash = {} 
ko.utils.arrayForEach(this.filter, function(f) { 
    this.hash[f] = true 
}.bind(this)) 

....skip a bit of code, then.... 

match = ko.utils.arrayFirst(tags, function(tag) { 
    return this.hash.hasOwnProperty(tag) 
}.bind(this)) 

(210)는 observableArrays가 하나가 observableArray에 항목을 하나씩 추가하는 대신 이동에 설정되어 있는지 확인 목록은

// Once all the new items are ready, put them into the observableArray 
    // on one go. 
    new_items = this.items().concat(new_items) 
    this.items(new_items) 

일치,이 그것이 ko.computed에있는 것을 통해 이루어집니다.이 목록은 변경되지 않았습니다 따라서 경우, 계산 된 값이 변경되지해야하며, KO는 DOM의이 비트 다시 렌더링 할 필요가 없습니다 : 마지막으로

// Find the matches for this list. 
this.matches = ko.computed(function() { 
    var matches = [] 

    ... do the logic ... 

    // Done. 
    return matches 
}, this) 

를, 내가 평가하는 테스트 버튼을 몇 추가 공연. 하나의 버튼은 다양한 임의의 태그가있는 1000 개의 항목을 추가하고 브라우저에는 4 초가 걸리고, 이는 DOM을 조작하는 KO에 전적으로 의존합니다. deferUpdates=true 여기에 도움이; 그것 없이는 10 초 정도 걸립니다. deferUpdates는 DOM을 그리기 전에 모든 계산이 평가되도록 보장하기 때문입니다. DOM이 없으면 DOM이 더 자주 그려집니다.

다른 테스트는 항목 중 하나를 수정하며 한 목록에서 다른 목록으로 빠르게 다시 그려집니다 - 변경 사항은 브라우저에서 즉시 나타납니다. 계산 된 각 계산은이 변경에 의해 재평가되지만 KO의 종속성 추적은 전체 목록을 다시 그리지 않고 차이를 다시 그릴 수있어 빠르게 만듭니다.

+1

아마도 코드를 더 빨리 작동시키는 가장 좋은 방법은'deferUpdates = true'를 사용하는 것입니다. –

+0

방금 ​​프로젝트를 KO3.4로 업그레이드했는데이 질문에 관심이있는 대부분의 이유는 다음을위한 deferUpdates에 대해 배우는 것이 었습니다. 자기. 오늘의 일은 내 앱에서 시도하는 것입니다. – sifriday

+0

자세한 답변을 기다려 주셔서 대단히 감사합니다. 지연된 업데이트가 이미 적용되어 있으며, 귀하의 의견을 게시했으며 이번 주말에 작업 할 예정입니다. 나는 명백한 알고리즘이 빠져 있는지 궁금해했다.하지만 나는 당신의 요점으로 생각하기에 속도를 늦추고 시작할 수있을만큼 의지 만 생각할 수있다. 나중에 다른 구조로 다시 작성하고 싶지 않았다. +1 그리고 나는 약간의 포인트를 얻을 수 있도록 받아 들일 것이다 :-) –