如何使用lodash重构这个double forLoop?

时间:2016-08-18 16:43:40

标签: javascript for-loop refactoring lodash

我有selectedTags,最多可容纳3个标签。 vm.tags可能包含数千个,很可能只包含数百个我需要比较的标记。

如果3个标签的ID与vm.tags内的标签的ID匹配,我需要打开它们的边框。还有3个边框:border1border2border3

const tagsColorCheck = () => {
    let name, selected, its_ticker;
    let selectedTags = TagsFactory.retrieveTickerTags('onlyTags');

    if (selectedTags.length > 0) {
        for (let i=0; i<vm.tags.length; i++) {
            for (let j=0; j<selectedTags.length; j++) {
                if (selectedTags[j].term_id == vm.tags[i].term_id) {
                    name       = 'border'+ ( j + 1 );
                    selected   = 'selected';
                    its_ticker = 'its_ticker';

                    vm.tags[i][name]     = true;
                    vm.tags[i][selected] = true;
                    vm.tags[i][its_ticker] = selectedTags[j].its_ticker;
                }
            }
        }
    }
};

到目前为止,我正在处理(_.each):

const tagsColorCheck = () => {
    let name, selected, its_ticker, vmTerm, term_1, term_2, term_3, ticker_1, ticker_2, ticker_3;
    let selectedTags = TagsFactory.retrieveTickerTags('onlyTags');

    if (!_.isEmpty(selectedTags)) {
        vmTerm = R.findIndex(R.propEq('term_id', selectedTags[0].term_id))(vm.tags);
    }

    if (selectedTags[0]) { term_1 = parseInt(selectedTags[0].term_id); ticker_1 = selectedTags[0].its_ticker; }
    if (selectedTags[1]) { term_2 = parseInt(selectedTags[1].term_id); ticker_2 = selectedTags[1].its_ticker; }
    if (selectedTags[2]) { term_3 = parseInt(selectedTags[2].term_id); ticker_3 = selectedTags[2].its_ticker; }

    _.each(vm.tags, (tag) => {
        if (tag.term_id === term_1) {
            tag.selected = true;
            tag.border1  = true;
            tag.its_ticker = ticker_1;
        }

        if (tag.term_id === term_2) {
            tag.selected = true;
            tag.border2  = true;
            tag.its_ticker = ticker_2;
        }

        if (tag.term_id === term_3) {
            tag.selected = true;
            tag.border3  = true;
            tag.its_ticker = ticker_3;
        }
    })
};

这个(for of loop):

const tagsColorCheck = () => {
    let name, selected, its_ticker, vmTerm, term_1, term_2, term_3, ticker_1, ticker_2, ticker_3;
    let selectedTags = TagsFactory.retrieveTickerTags('onlyTags');

    const borderRizeTag = (tag) => {
        if (tag.term_id === term_1) {
            tag.selected = true;
            tag.border1  = true;
            tag.its_ticker = ticker_1;
        }

        if (tag.term_id === term_2) {
            tag.selected = true;
            tag.border2  = true;
            tag.its_ticker = ticker_2;
        }

        if (tag.term_id === term_3) {
            tag.selected = true;
            tag.border3  = true;
            tag.its_ticker = ticker_3;
        }
        return tag;
    }

    if (!_.isEmpty(selectedTags)) {
        vmTerm = R.findIndex(R.propEq('term_id', selectedTags[0].term_id))(vm.tags);
    }

    if (selectedTags[0]) { term_1 = parseInt(selectedTags[0].term_id); ticker_1 = selectedTags[0].its_ticker; }
    if (selectedTags[1]) { term_2 = parseInt(selectedTags[1].term_id); ticker_2 = selectedTags[1].its_ticker; }
    if (selectedTags[2]) { term_3 = parseInt(selectedTags[2].term_id); ticker_3 = selectedTags[2].its_ticker; }

    for (let tag of vm.tags) {
        console.log(tag);
        tag = borderRizeTag(tag);
    }

    console.log('vmTerm',vmTerm);
};

3 个答案:

答案 0 :(得分:3)

ES6小提琴运行:http://www.es6fiddle.net/is0prsq9/(注意,复制整个文本,并将其粘贴到浏览器控制台或节点REPL中,然后检查tags的值以查看结果)< / p>

这不是lodash,你并不需要ES6结构。相关代码:

const tagsColorCheck = () => {

  let tags = TagsFactory.retrieveTickerTags('onlyTags')

  sel.forEach( (s,i) =>
    tags.filter(t => t.term_id === s.term_id).forEach( t => {
      t['border' + (i+1)] = true
      t.selected = true
      t.its_ticker = s.its_ticker
    })
  )

  return tags
}

如果您是用函数式语言编写的,那么您将可以访问list comprehension并且它会更清晰一些。从本质上讲,这是(for every x in a and y in b)的一个非常明显的例子,所以列表理解是你需要的,但你没有在javascript(mozilla has it中,但在该领域之外没有用。)

结果是一种有点功能的方法 - 然而,它在纯javascript中永远不会真正起作用。功能范例最重要的好处可能是immutable data structures,您可以在其中撰写新列表。相反,你只需在这里修改它们,这实际上根本不是很有用。尽管如此,如果你更喜欢each方法来增加文字增量,正如你在上面所做的那样,而且正如我在帖子中所做的那样,那么它是一种(尽管速度较慢但可以说更清晰)更好的方法。

答案 1 :(得分:1)

找出一个很棒的解决方案! :D同时使用_lodashramda

如下所示,立即each可以更快地推理,然后使用R.equals来比较term_ids是否匹配。然后在正确的tag对象上设置键的值。

if (!_.isEmpty(selectedTags)) {
    _.each(vm.tags, tag => {

        _.each(selectedTags, (selectedTag, index) => {
            let areTermIDsSame = R.equals;

            if (areTermIDsSame(parseInt(selectedTag.term_id), parseInt(tag.term_id))) {
                name       = 'border'+ ( index + 1 );
                selected   = 'selected';
                its_ticker = 'its_ticker';

                tag[name]       = true;
                tag[selected]   = true;
                tag[its_ticker] = selectedTag.its_ticker;
            }
        });
    })
}

答案 2 :(得分:1)

这个想法很简单 - 按term_id创建所有标签的索引。迭代选定的标签。如果在标签索引中通过id找到标签,则通过为具有新属性的对象来改变它。

btw - 唯一需要lodash的是_.keyBy(),如果您不想使用lodash,可以使用Array.prototype.reduce轻松完成。

/** mocked vm **/

const vm = {
	tags: [{ term_id: 1 }, { term_id: 2 }, { term_id: 3 }, { term_id: 4 }, { term_id: 5 }, { term_id: 6 }]
}

/** mocked TagsFactory **/

const TagsFactory = {
	retrieveTickerTags: () => [{ term_id: 1, its_ticker: 'ticker 1' }, { term_id: 4, its_ticker: 'ticker 4' }, { term_id: 5, its_ticker: 'ticker 5' }]
};

const tagsColorCheck = () => {
  const selectedTags = TagsFactory.retrieveTickerTags('onlyTags');

  if (selectedTags.length === 0) { // if selectedTags is empty exit
    return;
  }

  const vmTagsIndex = _.keyBy(vm.tags, (tag) => tag.term_id); // create an index of tags by term_id

  selectedTags.forEach(({
    term_id, its_ticker
  }, index) => { // loop through selectd tags and retreive term_id and its_ticker from the current selected tag
    const tag = vmTagsIndex[term_id]; // find the tag in the vmTagsIndex

    if (!tag) { // if the id doesn't exist in vmTagsIndex exit
      return;
    }

    Object.assign(tag, { // mutate the tag by assigining it an object with the available properties
      selected: true,
      [`border${index + 1}`]: true,
      its_ticker
    });
  });
};

tagsColorCheck();

console.log(vm.tags);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>