JavaScript:如何基于坐标的相似值对数据进行分组
Hey there! Let's work through this grouping problem you've got—since you mentioned wanting to use reduce, that's exactly the right tool for the job. Let's break down the logic step by step, then jump into code tailored to your scatter plot use case.
First, we need a helper function to judge if two points qualify as "similar" based on your rules:
- X values differ by 1 year or less
- Y values differ by 10,000 or less
const isSimilar = (pointA, pointB) => { const xDifference = Math.abs(pointA.coordinates.x - pointB.coordinates.x); const yDifference = Math.abs(pointA.coordinates.y - pointB.coordinates.y); return xDifference <= 1 && yDifference <= 10000; };
reduce We'll use reduce to build our groups incrementally. For each point, we check if it can join any existing group (by verifying it's similar to at least one point in the group). If a match is found, we add it to that group; if not, we create a new group for it.
const groupSimilarPoints = (data) => { return data.reduce((groups, currentPoint) => { // Find an existing group where the current point matches at least one member const matchingGroup = groups.find(group => group.some(point => isSimilar(point, currentPoint)) ); if (matchingGroup) { matchingGroup.push(currentPoint); } else { groups.push([currentPoint]); } return groups; }, []); };
Next, we separate the results into meaningful groups (with 2+ points) and ungrouped data (points that have no similar matches):
const processDataForPlot = (rawData) => { const allGroups = groupSimilarPoints(rawData); const validGroups = []; const ungroupedPoints = []; allGroups.forEach(group => { if (group.length > 1) { validGroups.push(group); } else { ungroupedPoints.push(...group); // Flatten single-point groups into the ungrouped array } }); // Return structure: [arrayOfValidGroups, arrayOfUngroupedPoints] return [validGroups, ungroupedPoints]; };
Since you want to show the point with the smallest Y value per group (and reveal the rest on click), we can transform the grouped data into a plot-ready format:
const preparePlotData = (processedData) => { const [groups, ungrouped] = processedData; // Process each valid group to get the display point (min Y) and all group points const groupPlotData = groups.map(group => { const minYPoint = group.reduce((min, point) => point.coordinates.y < min.coordinates.y ? point : min, group[0] ); return { displayPoint: minYPoint, allPoints: group }; }); // Add ungrouped points as individual display items const ungroupedPlotData = ungrouped.map(point => ({ displayPoint: point, allPoints: [point] })); return [...groupPlotData, ...ungroupedPlotData]; };
Let's run this with your example data to see how it works:
const sampleData = [ { otherData: "data1", coordinates: { x: 2022, y: 149832 } }, { otherData: "data2", coordinates: { x: 2023, y: 153729 } }, { otherData: "data3", coordinates: { x: 2024, y: 156800 } }, { otherData: "data4", coordinates: { x: 2024, y: 195233 } }, { otherData: "data5", coordinates: { x: 2035, y: 341978 } } ]; // Get grouped and ungrouped data const processed = processDataForPlot(sampleData); // Prepare data for plotting const plotReadyData = preparePlotData(processed); console.log(plotReadyData);
Output Explanation:
- The first valid group includes points 1, 2, 3 (each adjacent X differs by 1, and Y differences are all under 10k)
- Points 4 and 5 don't match any other points (X difference between 4 and 5 is 11, way over the 1-year limit), so they end up in the ungrouped array
- The plot-ready data will have 3 display items: one for the first group (showing the 2022 point, since it has the smallest Y), and two individual items for points 4 and 5.
内容的提问来源于stack exchange,提问作者Mosef

