根据条件修改对象数组并对最终结果进行排序。

huangapple go评论84阅读模式
英文:

Modify array of objects based on condition and sort the final result

问题

您可以使用以下代码来排序sampleData并获取前20个结果,确保只保留每个RouteId的一个对象:

// 首先按照RouteId和AreaCode的长度进行排序
sampleData.sort((a, b) => {
  if (a.RouteId !== b.RouteId) {
    return a.RouteId.localeCompare(b.RouteId);
  }
  return b.AreaCode.length - a.AreaCode.length;
});

// 使用Map来存储每个RouteId的第一个对象
const resultMap = new Map();
sampleData.forEach(item => {
  if (!resultMap.has(item.RouteId)) {
    resultMap.set(item.RouteId, item);
  }
});

// 获取前20个结果
const finalResult = Array.from(resultMap.values()).slice(0, 20);

// 输出finalResult
finalResult.forEach(item => {
  console.log(item);
});

这段代码首先按照RouteId升序和AreaCode长度降序对sampleData进行排序。然后,使用Map来存储每个RouteId的第一个对象,并获取前20个结果,以确保只保留每个RouteId的一个对象。最后,输出finalResult数组。

英文:
sampleData = [{
  RouteId: "1",
  InDirection: "1",
  AreaCode: ["41108", "41109", "41110", "41111"],
}, {
  RouteId: "1",
  InDirection: "2",
  AreaCode: ["41108", "41109", "411011"],
}, {
  RouteId: "2",
  InDirection: "1",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "2",
  InDirection: "2",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "3",
  InDirection: "1",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "3",
  InDirection: "2",
  AreaCode: ["41112", "41114","41108", "41109", "41110"],
}, {
  RouteId: "4",
  InDirection: "1",
  AreaCode: ["41112", "41114","41108", "41110" , "41120", "41121"],
}, {
  RouteId: "4",
  InDirection: "2",
  AreaCode: ["41112", "41114"],
}]

I want to sort above sampleData based on number of entries in AreaCodes and get top 20 results. But I only want one object every RouteId. Every RouteId can have two types of InDirection = 1 or 2. So in the above result would like to removed

{
  RouteId: "1",
  InDirection: "2",
  AreaCode: ["41108", "41109", "411011"],
}

since it less entires on AreaCode as compared to InDirection= 1

so the final sorted result should be

finalResult = [{
  RouteId: "1",
  InDirection: "1",
  AreaCode: ["41108", "41109", "41110", "41111"],
}, {
  RouteId: "2",
  InDirection: "1",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "3",
  InDirection: "2",
  AreaCode: ["41112", "41114","41108", "41109", "41110"],
}, {
  RouteId: "4",
  InDirection: "1",
  AreaCode: ["41112", "41114","41108", "41110" , "41120", "41121"],
}]

Here I got so far:

    const filteredItems = sampleData.filter(item => {
const otherItem = transformedData.find(i => i.RouteId === item.RouteId && i.InDirection !== item.InDirection);
if (otherItem) {
    return item.AreaCode.length > otherItem.AreaCode.length;
} else {
    return true;
} 
});

But this missed condition where length of AreaCode is equal and the final result is not sorted.

答案1

得分: 1

我个人认为filter()在这里不是最佳解决方案。

如果你知道提供的结构总是在每两个相邻的元素中都有相同的RouteId,那么你可以将数组分成大小为2的块,然后使用map()函数对每个块进行比较,基于AreaCode.length来选择较大的元素。

const desired = chunkN(2, sampleData).map(([inDirection1, inDirection2]) => (
  inDirection1.AreaCode.length >= inDirection2.AreaCode.length
  ? inDirection1
  : inDirection2
));

上面的代码使用了我在下面片段中定义的chunkN()辅助函数。它实际上将数组分割成大小为N的块。然后我们使用条件(三元)运算符来选择具有最大AreaCode.length的是inDirection1还是inDirection2


如果提供的元素不是严格结构化的,可以以任何顺序存在,并且可能有多于两个inDirection选项。我建议首先根据RouteId对所有元素进行分组,然后按照AreaCode.length以降序方式对每个组进行排序并选择第一个元素。

const groups = groupBy(item => item.RouteId, sampleData);
const desired = Array.from(groups.values(), (group) => (
  // 升序 = a - b,降序 = b - a
  group.sort((a, b) => b.AreaCode.length - a.AreaCode.length)[0]
));

groupBy()辅助函数是一个返回Map实例的辅助函数。在上面的示例中,我们使用RouteId作为键。该值是与此键匹配的项目数组。

英文:

I personally don't think filter() is the best solution here.

If you know that the provided structure always provides exactly two items with the same RouteId after each other. Then you can chunk the array in chunks of size 2. Then map() each chunk by comparing the two elements based on AreaCode.length.

const desired = chunkN(2, sampleData).map(([inDirection1, inDirection2]) => (
  inDirection1.AreaCode.length >= inDirection2.AreaCode.length
  ? inDirection1
  : inDirection2
));

The code above uses a chunkN() helper that I've defined in the snippet below. It essentially cuts up the array in chunks of size N. Then we use the conditional (ternary) operator to select if inDirection1 or inDirection2 is the item with the largest AreaCode.length.


If the provided elements are not strictly structured and can be in any order and there are possibly more then two inDirection options. I would suggest first grouping all the elements based on RouteId. Then sort() each group based on AreaCode.length in a descending manner and select the first item.

const groups = groupBy(item => item.RouteId, sampleData);
const desired = Array.from(groups.values(), (group) => (
  // ascending = a - b, descending = b - a
  group.sort((a, b) => b.AreaCode.length - a.AreaCode.length)[0]
));

The groupBy() helper is a helper that returns a Map instance. In the example above RouteId is used as the key. The value is an array of items that match this key.

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-js -->

function solutionA(sampleData) {
  const desired = chunkN(2, sampleData).map(([inDirection1, inDirection2]) =&gt; (
    inDirection1.AreaCode.length &gt;= inDirection2.AreaCode.length
    ? inDirection1
    : inDirection2
  ));
  console.log(&quot;solutionA&quot;, desired);
}

function solutionB(sampleData) {
  const groups = groupBy(item =&gt; item.RouteId, sampleData);
  const desired = Array.from(groups.values(), (items) =&gt; (
    // ascending = a - b, descending = b - a
    items.sort((a, b) =&gt; b.AreaCode.length - a.AreaCode.length)[0]
  ));
  console.log(&quot;solutionB&quot;, desired);
}

// helpers
function chunkN(n, array) {
  const chunks = [];
  for (let index = 0; index &lt; array.length; index += n) {
    chunks.push(array.slice(index, index + n));
  }
  return chunks;
}

function groupBy(fnKey, iterable) {
  const groups = new Map();
  for (const item of iterable) {
    const key = fnKey(item);
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(item);
  }
  return groups;
}

// data + run solutions
const data = [{
  RouteId: &quot;1&quot;,
  InDirection: &quot;1&quot;,
  AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;, &quot;41111&quot;],
}, {
  RouteId: &quot;1&quot;,
  InDirection: &quot;2&quot;,
  AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;411011&quot;],
}, {
  RouteId: &quot;2&quot;,
  InDirection: &quot;1&quot;,
  AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}, {
  RouteId: &quot;2&quot;,
  InDirection: &quot;2&quot;,
  AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}, {
  RouteId: &quot;3&quot;,
  InDirection: &quot;1&quot;,
  AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}, {
  RouteId: &quot;3&quot;,
  InDirection: &quot;2&quot;,
  AreaCode: [&quot;41112&quot;, &quot;41114&quot;,&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;],
}, {
  RouteId: &quot;4&quot;,
  InDirection: &quot;1&quot;,
  AreaCode: [&quot;41112&quot;, &quot;41114&quot;,&quot;41108&quot;, &quot;41110&quot; , &quot;41120&quot;, &quot;41121&quot;],
}, {
  RouteId: &quot;4&quot;,
  InDirection: &quot;2&quot;,
  AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}];
solutionA(data);
solutionB(data);

<!-- end snippet -->


Both chunkN() and groupBy() are generic helpers that can be used in lots of other scenarios. You can also achieve the same without helpers (like shown in the two codeblocks below). But defining these helpers separates the thing you want to do (finding the item with the most AreaCodes per RouteId) from the general purpose logic, like grouping or chunking.

const desired = [];
for (let index = 0; index &lt; sampleData.length; index += 2) {
  const inDirection1 = sampleData[index];
  const inDirection2 = sampleData[index + 1];
  desired.push(
    inDirection1.AreaCode.length &gt;= inDirection2.AreaCode.length
    ? inDirection1
    : inDirection2
  );
}
const groups = new Map();
for (const item of sampleData) {
  if (!groups.has(item.RouteId)) groups.set(item.RouteId, []);
  groups.get(item.RouteId).push(item);
}

const desired = Array.from(groups.values(), (group) =&gt; (
  // ascending = a - b, descending = b - a
  group.sort((a, b) =&gt; b.AreaCode.length - a.AreaCode.length)[0]
));

答案2

得分: 1

我肯定会将这个问题分成几个步骤。首先,我们需要按AreaCode计数对元素进行排序,然后我们需要取前二十个,但要注意每个RouteId只能选取一个。最后,根据您请求的输出,似乎我们应该根据数据中的原始顺序(或RouteId)重新对它们进行排序。我觉得最后一步非常奇怪。如果您根据某种顺序选择它们,为什么在缩短列表时不保留该顺序呢?在下面的代码中,跳过该步骤非常容易。只需删除最后的sort调用。

这里我们的示例只取前三个,而不是前二十个,因为我们没有足够的数据。

这里有一个实用函数by,它创建了可传递给sort的比较函数。它接受一个从要排序的项到可排序项(例如数字、字符串或日期)的函数,并可选地指定方向(除了默认的“ASCENDING”外,任何其他方向都表示降序排序),然后返回一个函数,当提供两个排序项时,它将返回-1、0或+1。将此函数输入到Array.prototype.sort中将根据该函数的结果对这些项进行排序。

然后我们有一个辅助函数takeFirstOneOfEachUpTo,它选择其输入的前n个项目,但要求对于提供的函数,我们不重复输出具有相同值的输入。这是一个相当简单的递归,与基于reduce的解决方案相比,它的优点是可以提前停止。对于数字如20来说,这很好。如果要收集前1000个,我们可能需要一个基于reduce的解决方案,它不具有递归性。该函数维护一个已经见过的值的Set,并跳过后续的值。这意味着我们的函数可能应该生成一个基本值,或者以某种方式从固定的参考值列表中选择。我们将在RouteId字符串上使用它,所以这不是问题。

我们的主要函数是process,因为我没有足够的上下文为其提供更有意义的名称。它首先按其AreaCode数组长度降序对输入进行排序,然后调用我们的辅助函数,传递给它20(或3或我们要收集的任何数量)以及获取RouteId的函数,从而获取前20个元素,但每个RouteId只能选择一个。最后——我认为是不必要的步骤——根据它们在原始列表中的位置对结果进行排序。

这个分解对我来说非常有意义。这不是解决问题的唯一方法。Peter Seliger提出了一个异议,基于之前的问题,该问题使用了一种较不紧凑的格式来生成此问题的输入结构。他有一点。也许有一种有用的方法可以根据您的原始数据生成最终的格式。

实际上,从理论上讲,还有一种更节省时间的方式来做到这一点。我知道这是因为我们在您的输入数据上使用了sort,这意味着这至少是一个O(n log(n))的技术。但是,收集列表的最大值或固定k的k个最大元素已知是一个线性问题,O(n)。从理论上讲,因此至少有一种更快的解决方案,至少对于非常长的列表来说是如此。但除非您在性能方面遇到问题,否则我不会费心。我所知道的用于k-largest的技术需要随着k的大小增长的代码量,或者其运行时间随着k的平方增长(仍然相对于n固定,但足够大,以至于对于除了极长的列表之外的任何事情都很麻烦)。可能还有其他我不知道的不具有这些限制的方法,但再次,只有在出现性能问题的情况下才会搜索它们。

因此,出于这个原因,如果我要基于原始数据解决这个问题,我会完全按照这里的方法,将您提供的中间格式作为此问题的输入。

Peter的(遗憾的是已删除的)答案提供了另一种方法。我希望他完成并恢复它,因为它提供了一种不同结构的解决问题的方式,并以一种有趣的方式使用了您的原始数据。

英文:

I would definitely break this into several steps. First, we need to sort the elements by the count of AreaCodes, then we need to take the top twenty, subject to the constraint that we can have only one per RouteId, and then, it seems, from your requested output, we should re-sort these according to their original order in the data (or according to their RouteId.) This last step seems really strange to me. If you're taking them according to an ordering, why wouldn't you keep that ordering when you shorten the list? In the code below, it's trivially easy to skip that step. Just remove the final sort call.

Here our sample only takes the first three, not the first twenty, as we don't have enough data for that.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const by = (fn, dir = &#39;ASCENDING&#39;) =&gt; (a, b, x = fn(a), y = fn(b)) =&gt; 
  (dir === &#39;ASCENDING&#39; ? 1 : -1) * (x &lt; y ? -1 : x &gt; y ? 1 : 0)

const takeFirstOneOfEachUpTo = (n, fn) =&gt; ([x, ...xs], m = new Set(), k = fn(x)) =&gt;
  x == undefined || n &lt; 1 
    ? [] 
  : m.has(k)
    ? takeFirstOneOfEachUpTo(n, fn)(xs, m)
  : [x, ...takeFirstOneOfEachUpTo(n -1, fn)(xs, m.add(k))]


const process = (n) =&gt; (xs) =&gt; 
  takeFirstOneOfEachUpTo (n, x =&gt; x.RouteId) (
    [...xs].sort((by(x =&gt; x.AreaCode.length, &#39;DESCENDING&#39;)))
  ).sort(by(x =&gt; xs.indexOf(x))) // or .sort(by(x =&gt; x.RouteId)) // or skip altogether

const sampleData = [{RouteId: &quot;1&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;, &quot;41111&quot;]}, {RouteId: &quot;1&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;411011&quot;]}, {RouteId: &quot;2&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;]}, {RouteId: &quot;2&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;]}, {RouteId: &quot;3&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;]}, {RouteId: &quot;3&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;, &quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;]}, {RouteId: &quot;4&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;, &quot;41108&quot;, &quot;41110&quot;, &quot;41120&quot;, &quot;41121&quot;]}, {RouteId: &quot;4&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;]}]

console.log(process(3)(sampleData))

<!-- language: lang-css -->

.as-console-wrapper {max-height: 100% !important; top: 0}

<!-- end snippet -->

Here we have a utility function by, which makes comparators to pass to sort. It takes a function from your sort item to something sortable with &lt; (such as a number, a string or a date), and optionally a direction (anything other than the default, 'ASCENDING' is taken to mean descending sort), and returns a function which will return -1, 0, or +1 when supplied two sort items. Feeding this into Array.prototype.sort, will sort these items according to the result of that function.

Then we have a helper function, takeFirstOneOfEachUpTo, which selects the first n items of its input, subject to the constraint that for the supplied function, we don't repeat inputs for which the function yields the same value. This is a fairly simple recursion, and it has the advantage over a reduce-based solution of stopping early. It's fine for numbers like 20. If you were to be collecting the top 1000, we might want a reduce-based solution instead, which doesn't have the recursion. This function maintains a Set of values its already seen and skips subsequent ones. That means that our function should probably generate a primitive value, or choose somehow from a fixed list of reference values. We're going to use it with the RouteId strings, so that's not a problem.

Our main function is process, named because I don't have enough context to give it a more meaningful one. It first sorts the inputs descending by the lengths of their AreaCode arrays, then it calls our helper function, passing it 20 (or 3 or however many we want to collect) and a function which gets the RouteId, thus grabbing the top 20 elements, but only one per RouteId. Finally -- in the step I find unnecessary -- it sorts the results according to their position in the original list.


This breakdown makes a great deal of sense to me. It is not the only way to approach the problem. Peter Seliger has raised an objection, based on an earlier question which used a less compact format to generate the input structure of this question. He has a point. Perhaps there's a useful way to generate this final format based on your original data.

In fact, there is technically an asymptotically more time-efficient manner of doing this. I know this because we are using a sort on your input data, meaning this is at minimum an O(n log(n)) technique. But collecting the maximum of a list, or the k-largest elements for a fixed k is known to be a linear problem, O(n). Theoretically, then, there is a faster solution, at least for extremely long lists. But unless you hit a performance wall with this, I would not bother. The techniques I know for k-largest require an amount of code that grows with the size of k, or one whose running time grows quadratically with k (still fixed with respect to n, but large enough to be annoyingly impractical for anything but extremely long lists.) There may be others I don't know that don't have these limitations, but again, I would search for them only with a demonstrated performance problem.

For this reason, if I were to solve this problem based on the original data, I would do it exactly as here, using the intermediate format you present as input to this question.

Peter's (sadly deleted) answer offers another approach. I hope he finishes it and restores it, as it offers an architecturally different way of approaching this, and uses your original data in an interesting way.

答案3

得分: 1

下面提供的方法考虑了 OP 句子的以下部分...

> "我想根据AreaCodes中的条目数量对上述sampleData进行排序,并获取前20个结果。"

... 并进一步假设 OP 想要检索前20个唯一的RouteId数据项,每个项都具有可能的最大AreaCode数组。此外,允许只有其中一个具有相同RouteId值但具有不同InDirection值的项目成为“前20名”中的一部分(即使相关但被丢弃的项目的AreaCode数组仍比所有其他源数据项的数组更大)。

因为 OP 在评论中开始提及...

> "由于在实际情况中sampleData非常庞大,是否可能..."

... 选择了以下方法。

似乎不可避免地必须对提供的源数据数组(sampleData)进行排序。排序过程已经是最昂贵的部分。人们按照任何项的AreaCode数组的length值按降序对数组进行排序。

但不能简单地将排序后的数组限制为前20项。

必须迭代数组,并区分具有相同RouteId值和其他项。对于前者,必须实现一个任务,该任务选择具有较大AreaCode数组的项目(或在两者长度相等的情况下,根据 OP 的要求选择首次出现的项目)。

由于需要对迭代进行更多控制,选择了基于while的循环,它允许在达到“前20名”之前提前退出迭代。

为了加速从一对相关的相同RouteId值项目中选择正确项目的任务,该方法引入了基于Set的查找,可以基于项的RouteId检查是否已处理并收集到result数组中。

然后可以再次按每个项目的RouteId升序对后者进行排序,或者可以将result保留为原样。

英文:

The next provided approach takes into account following of the OP's sentences ...

> "I want to sort above sampleData based on number of entries in AreaCodes and get top 20 results."

... and it further assumes that the OP wants to retrieve the top 20 unique RouteId data-items with each item featuring the largest possible AreaCode array. In addition, only one of the items which feature the same RouteId value but have each a distinct InDirection value, is allowed to be part of the "Top 20" (even in case the related but discarded item features a still larger AreaCode array than all other source data items).

Because the OP started mentioning in one of the comments ...

> "Since in the real scenario sampleData is very large, is it possible ..."

... following approach has been chosen.

It seems to be unavoidable that one has to sort the provided source data array (sampleData) in its entirety. The sorting process already is the most expensive part. One does sort the array in descending order by any item's length value of the item's AreaCode array.

But one can not simply limit the sorted array to its first 20 items.

One has to iterate the array and has to make a distinction in between items that share the same RouteId value and the others. For the former, one has to implement a task which picks the item with the larger AreaCode array (or in case both are of equal length, according to the OP's requirements, the first occurring item ).

Since one needs to have a bit more control over the iteration, a while based loop has been chosen which allows the early exit in case of having reached the "Top 20" before having finished iterating the array entirely.

And in order to speed up the task which picks the correct item out of a pair of related same RouteId-value items, the approach introduces a Set based lookup where one, based on an item's RouteId, can check if the correct RouteId item already has been processed and collected into the result array.

The latter then can be sorted again ascending by each item's RouteId, or result could be simply left as is.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function getTopAmountOfUniqueRouteIdItemsOfLargestAreaCodeData(
  sourceData = [], topAmount = 20
) {
  // // in order to not mutate the passed `sourceData` reference.
  // sourceData = [...sourceData].sort( /* ... */ );

  // - the most expensive part of the approach.
  // - sort the provided data array in its entirety.
  sourceData
    // takes advantage of the `-` operator&#39;s type coercion.
    .sort((a, b) =&gt; b.AreaCode.length - a.AreaCode.length);

  // ... pushing data directly into the return value ..
  const result = [];
  // ... and accomplishing the necessary pick on a `Set`
  //     based lookup does speed up the rest of the task.
  const lookup = new Set;

  let collectionCount = 0;
  let item, idx = -1;

  while ((item = sourceData[++idx]) &amp;&amp; (collectionCount &lt; topAmount)) {
    // - keep iterating until no item has been left
    //   or until the top amount has been reached.
    const { RouteId } = item;

    if (!lookup.has(RouteId)) {
      lookup.add(RouteId);

      result.push(item);

      ++collectionCount;
    }
  }
  // ... either `result` as sorted return value ...
  return result.sort((a, b) =&gt; a.RouteId - b.RouteId);

  // // ... or, just returning `result` as is ...
  // return result;
}

const result =
  getTopAmountOfUniqueRouteIdItemsOfLargestAreaCodeData(sampleData);

console.log({ result });

<!-- language: lang-css -->

.as-console-wrapper { min-height: 100%!important; top: 0; }

<!-- language: lang-html -->

&lt;script&gt;
  const sampleData = [{
    RouteId: &quot;1&quot;,
    InDirection: &quot;1&quot;,
    AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;, &quot;41111&quot;],
  }, {
    RouteId: &quot;1&quot;,
    InDirection: &quot;2&quot;,
    AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;411011&quot;],
  }, {
    RouteId: &quot;2&quot;,
    InDirection: &quot;1&quot;,
    AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
  }, {
    RouteId: &quot;2&quot;,
    InDirection: &quot;2&quot;,
    AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
  }, {
    RouteId: &quot;3&quot;,
    InDirection: &quot;1&quot;,
    AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
  }, {
    RouteId: &quot;3&quot;,
    InDirection: &quot;2&quot;,
    AreaCode: [&quot;41112&quot;, &quot;41114&quot;,&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;],
  }, {
    RouteId: &quot;4&quot;,
    InDirection: &quot;1&quot;,
    AreaCode: [&quot;41112&quot;, &quot;41114&quot;,&quot;41108&quot;, &quot;41110&quot; , &quot;41120&quot;, &quot;41121&quot;],
  }, {
    RouteId: &quot;4&quot;,
    InDirection: &quot;2&quot;,
    AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
  }];
&lt;/script&gt;

<!-- end snippet -->

答案4

得分: 0

你想要使用 sortfilter 函数来处理数组,尽管可能还有其他方法可以做到这一点。

const sampleData = [{
  RouteId: "1",
  InDirection: "1",
  AreaCode: ["41108", "41109", "41110", "41111"],
}, {
  RouteId: "1",
  InDirection: "2",
  AreaCode: ["41108", "41109", "411011"],
}, {
  RouteId: "2",
  InDirection: "1",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "2",
  InDirection: "2",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "3",
  InDirection: "1",
  AreaCode: ["41112", "41114"],
}, {
  RouteId: "3",
  InDirection: "2",
  AreaCode: ["41112", "41114", "41108", "41109", "41110"],
}, {
  RouteId: "4",
  InDirection: "1",
  AreaCode: ["41112", "41114", "41108", "41110", "41120", "41121"],
}, {
  RouteId: "4",
  InDirection: "2",
  AreaCode: ["41112", "41114"],
}]

const routeIdsFound = []
const result = sampleData.sort(
  (a, b) => b.AreaCode.length - a.AreaCode.length
).filter(item => {
  if (!routeIdsFound.includes(item.RouteId)) {
    routeIdsFound.push(item.RouteId)
    return true
  } else {
    return false
  }
}).sort((a, b) => parseInt(a.RouteId) - parseInt(b.RouteId))

console.log(result);

你也可以考虑使用类似 Lodash 这样的外部库,也许 uniqBy 可能会引起你的兴趣(或者 sortedUniqBy ?)

英文:

You want to make use of sort and filter functions for arrays, though there may be other ways of doing thins.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const sampleData = [{
RouteId: &quot;1&quot;,
InDirection: &quot;1&quot;,
AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;, &quot;41111&quot;],
}, {
RouteId: &quot;1&quot;,
InDirection: &quot;2&quot;,
AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;411011&quot;],
}, {
RouteId: &quot;2&quot;,
InDirection: &quot;1&quot;,
AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}, {
RouteId: &quot;2&quot;,
InDirection: &quot;2&quot;,
AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}, {
RouteId: &quot;3&quot;,
InDirection: &quot;1&quot;,
AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}, {
RouteId: &quot;3&quot;,
InDirection: &quot;2&quot;,
AreaCode: [&quot;41112&quot;, &quot;41114&quot;, &quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;],
}, {
RouteId: &quot;4&quot;,
InDirection: &quot;1&quot;,
AreaCode: [&quot;41112&quot;, &quot;41114&quot;, &quot;41108&quot;, &quot;41110&quot;, &quot;41120&quot;, &quot;41121&quot;],
}, {
RouteId: &quot;4&quot;,
InDirection: &quot;2&quot;,
AreaCode: [&quot;41112&quot;, &quot;41114&quot;],
}]
const routeIdsFound = []
const result = sampleData.sort(
(a, b) =&gt; b.AreaCode.length - a.AreaCode.length
).filter(item =&gt; {
if (!routeIdsFound.includes(item.RouteId)) {
routeIdsFound.push(item.RouteId)
return true
} else {
return false
}
}).sort((a, b) =&gt; parseInt(a.RouteId) - parseInt(b.RouteId))
console.log(result);

<!-- end snippet -->

You may also want to use something like Lodash, (if you're not opposed to using an external library), perhaps uniquBy might be something of interest (or sortedUinqBy ?)

答案5

得分: 0

你可以按步骤完成此操作

  • 首先,使用 Array#sort 按照大多数 AreaCode 降序排序
  • 然后,使用 Array#reduce 来排除任何已存在于元素索引之前的 RouteId 的元素

以下是代码的翻译部分:

const 
      sampleData = [{ RouteId: "1", InDirection: "1", AreaCode: ["41108", "41109", "41110", "41111"], }, { RouteId: "1", InDirection: "2", AreaCode: ["41108", "41109", "411011"], }, { RouteId: "2", InDirection: "1", AreaCode: ["41112", "41114"], }, { RouteId: "2", InDirection: "2", AreaCode: ["41112", "41114"], }, { RouteId: "3", InDirection: "1", AreaCode: ["41112", "41114"], }, { RouteId: "3", InDirection: "2", AreaCode: ["41112", "41114","41108", "41109", "41110"], }, { RouteId: "4", InDirection: "1", AreaCode: ["41112", "41114","41108", "41110" , "41120", "41121"], }, { RouteId: "4", InDirection: "2", AreaCode: ["41112", "41114"], }],
      
      sortFiltered = sampleData.sort((a,b) => b.AreaCode.length - a.AreaCode.length).reduce(
          (filtered,cur) => filtered.find(a => a.RouteId === cur.RouteId) ? filtered : [...filtered,cur], []
      );
      
      
console.log( sortFiltered );

请注意,此处仅提供了代码的翻译部分。

英文:

You can do this in steps

  • First, sort with Array#sort in descending order of most AreaCodes
  • Then, use Array#reduce to exclude any element whose RouteId already exists prior to the elements index

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const 
sampleData = [{ RouteId: &quot;1&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;, &quot;41111&quot;], }, { RouteId: &quot;1&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41108&quot;, &quot;41109&quot;, &quot;411011&quot;], }, { RouteId: &quot;2&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;], }, { RouteId: &quot;2&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;], }, { RouteId: &quot;3&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;], }, { RouteId: &quot;3&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;,&quot;41108&quot;, &quot;41109&quot;, &quot;41110&quot;], }, { RouteId: &quot;4&quot;, InDirection: &quot;1&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;,&quot;41108&quot;, &quot;41110&quot; , &quot;41120&quot;, &quot;41121&quot;], }, { RouteId: &quot;4&quot;, InDirection: &quot;2&quot;, AreaCode: [&quot;41112&quot;, &quot;41114&quot;], }],
sortFiltered = sampleData.sort((a,b) =&gt; b.AreaCode.length - a.AreaCode.length).reduce(
(filtered,cur) =&gt; filtered.find(a =&gt; a.RouteId === cur.RouteId) ? filtered : [...filtered,cur], []
);
console.log( sortFiltered );

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年7月10日 21:39:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76654328.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定