Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dedupe attributions that are substrings of others #2453

Merged
merged 2 commits into from
Apr 20, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions js/ui/control/attribution.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ Attribution.prototype = util.inherit(Control, {
}
}

// remove any entries that are duplicates or strict substrings of another entry.
attributions.sort();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you sort the attributions before filtering them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucaswoj i figured sorting the list first means that, in the filter function for the i-th item, you can safely start the loop at i+1. Without this, you have to check every item, and I think it gets a little weird to correctly filter out exact duplicates.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether you sort or not, you're comparing every attribution string to every other attribution string. I don't think the sort is necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're comparing every attribution string to every other attribution string

Agreed, but it's also directional, since we're looking for a substring match and want to keep the longer of the two if there's a match.

I agree the sort isn't necessary, but removing it does make the logic in the filter function a little more particular... I think it would then need to be something like:

attribution = attribution.filter(function (a, i) {
  // check if `a` is a substring of another string in the list
  var isSubsumedByAnother = attribution.some(function (b, j) {
    // skip if we're checking the same element
    if (i === j) { return false }
    // if a === b, then only call it a match if a's index is higher; otherwise
    // we would filter out both elements when there's an exact match
    if (a === b) { return i > j }
    // check if a is a substring of b
    if (b.indexOf(a) >= 0) { return true }
  })
  return !isSubsumedByAnother
})

@lucaswoj I considered this, and ended up feeling that both approaches were equally bad... lemme know if you find this way clearer and I can change it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but it's also directional, since we're looking for a substring match and want to keep the longer of the two if there's a match.

That's a good point. We should sort by length not alphabetic ordering, then.

attributions = attributions.filter(function (attrib, i) {
for (var j = i + 1; j < attributions.length; j++) {
if (attributions[j].indexOf(attrib) >= 0) { return false; }
}
return true;
});

this._container.innerHTML = attributions.join(' | ');
this._editLink = this._container.getElementsByClassName('mapbox-improve-map')[0];
this._updateEditLink();
Expand Down
23 changes: 23 additions & 0 deletions test/manual/attribution.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@

new mapboxgl.Attribution({position: 'top-left'})
.addTo(map);

map.on('style.load', function () {
map.addSource('foo', new mapboxgl.GeoJSONSource({
data: {
type: 'FeatureCollection', features: []
}
}));
map.style.sources.foo.attribution = '<a href="https://www.mapbox.com/about/maps/" target="_blank">&copy; Mapbox</a> <a href="http://www.openstreetmap.org/about/" target="_blank">&copy; OpenStreetMap</a> <a class="mapbox-improve-map" href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a> &copy; Someone else like DG'

map.addSource('bar', new mapboxgl.GeoJSONSource({
data: {
type: 'FeatureCollection', features: []
}
}));
map.style.sources.bar.attribution = 'Another Source';

map.addSource('baz', new mapboxgl.GeoJSONSource({
data: {
type: 'FeatureCollection', features: []
}
}));
map.style.sources.baz.attribution = 'Another Source';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is anything blocking us from creating automated unit tests for this code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I meant to mention this in the PR: I looked and didn't find any other unit tests for Attribution, so I held back on adding one in case there was a reason not to. Happy to add a test, though.

});
</script>
</body>
</html>