按模式对数组进行排序 -- JavaScript



对于数组

['one', 'one', 'two', 'two',  'three', 'one']

使用模式 ["一"、"二"、"三"] 将其转换为

['one', 'two', 'three', 'one', 'two', 'one']

我的想法是

const sortArray = oldArray => {
let newArr = [];
while (newArr < oldArray.length) {
// loop through array
for (let i = 0; i < arr.length; i++) {
// loop through pattern
for (let j = 0; j < pattern.length; j++) {
// match
if (arr[i] === pattern[j]) {
// add item to new array
newArr.push(arr[i]);
// remove item from old array
arr.shift();
} else {
// push item to end of array
arr.push(arr[i]);
// remove item from array
arr.shift()
}
}
}
}
return newArray;
}

我可以使用地图来做到这一点,这是我习惯于解决这样的事情的方法,但是当涉及到仅遍历具有模式的数组时,我会感到非常困惑。 有什么建议吗?

用地图,这就是我的做法

let a = ['one', 'one', 'two', 'two',  'three', 'one'];
const printValues = (arr, pattern) => {
let map = {};
let a = [];
arr.forEach((v) => {
if (!map[v]) map[v] = 1;
else map[v]++;
})
while (a.length !== arr.length) {
pattern.forEach((v) => {
if (map[v] > 0) {
a.push(v);
map[v]--;
}
})
}
console.log(a);
}
console.log(printValues(a, ['one', 'two', 'three']))

我认为您的想法是正确的,但是您想先遍历模式数组以保持排序,然后再查找oldArray。在下面的解决方案中,我还使用了一个集合来存储已使用的索引。

const oldArray = ['one', 'one', 'two', 'two', 'three', 'one'];
const pattern = ['one', 'two', 'three'];
let newArray = [];
let added = new Set();
while (newArray.length < oldArray.length) {
for (let p of pattern) {
for (let i = 0; i < oldArray.length; i++) {
if (!added.has(i) && oldArray[i] === p) {
added.add(i);
newArray.push(p);
break;
}
}
}
}
console.log(newArray);

尝试以下操作:

  • 计算模式中所有元素的频率。
  • 而不是简单地迭代模式数组并不断逐个推送每个元素(直到每个元素计数为零(。

let arr = ['one', 'one', 'two', 'two',  'three', 'one','three'];
let freq = arr.reduce((a,curr)=>{
a[curr] = (a[curr] || 0)+1;
return a;
},{});
let len =  Object.keys(freq).length;
let result = [];
let pattern = ["one", "two", "three"];
let i = 0;
while(len){
if(freq[pattern[i]]){
result.push(pattern[i]);
freq[pattern[i]] --;
} else
len--;
i++;
i = i % pattern.length;
}
console.log(result);

这是一个有趣的问题!

注意:你并没有真正告诉如何处理模式中未知的元素。它们应该都出现在开头还是结尾,还是均匀分布?我决定忽略这一点。

如果您将模式视为生成新数组的种子,而不是现有数组的迭代约束,那么恕我直言,这个问题更容易解决。

您可以创建一个函数,该函数接受模式以及每个元素的频率:

createFromPattern({one: 3, two: 2, three:1}, ['one', 'two', 'three']);

生成频率很容易:

const count = list => list.reduce((acc, cur) => ({...acc, [cur]: (acc[cur] || 0) + 1}), {});
count(['one', 'one', 'two', 'two',  'three', 'one']);
//=> { one: 3, two: 2, three: 1 }

让我们可视化该函数的工作原理:

  1. { one: 3, two: 2, three: 1 }~>['one', 'two', 'three']
  2. { one: 2, two: 1, three: 0 }~>['one', 'two']
  3. { one: 1, two: 0, three: -1 }~>['one']
  4. { one: 0, two: -1, three: -2 }~>[]

如果聚合每个中间结果,则最终会得到最终的数组。这可以递归完成:

const createFromPattern = (opts, seed) => {
const newOpts = {...opts};
const pick = seed.reduce((acc, cur) => [...acc, ...(newOpts[cur] ? newOpts[cur]-- && [cur] : [])], []);
const stop = Math.max(...Object.values(newOpts)) <= 0;
return [].concat(pick, (!stop ? createFromPattern(newOpts, seed) : []));
};

总而言之:

const list = ['one', 'one', 'two', 'two',  'three', 'one']
const pattern = ['one', 'two', 'three']
const count = list => list.reduce((acc, cur) => ({...acc, [cur]: (acc[cur] || 0) + 1}), {});
const createFromPattern = (opts, seed) => {
const newOpts = {...opts};
const pick = seed.reduce((acc, cur) => [...acc, ...(newOpts[cur] ? newOpts[cur]-- && [cur] : [])], []);
const stop = Math.max(...Object.values(newOpts)) <= 0;
return [].concat(pick, (!stop ? createFromPattern(newOpts, seed) : []));
};
console.log(
createFromPattern(count(list), pattern)
);

其他答案很接近,但如果使用 Map,则不需要模式数组。添加密钥的顺序将是可以取出密钥的顺序。

您应该改用地图:

const arr = ['one', 'one', 'two', 'two', 'three', 'one'];
const map = arr.reduce(
(result, item) =>
result.set(item, (result.get(item) || []).concat(item)),
new Map(),
);
const transform = (arr) => {
const recur = (arr, result, index, max) => {
if (index === max) {
return result;
}
return recur(
arr,
result.concat(arr.map((item) => item[index])),
index + 1,
max,
);
};
return recur(
arr,
[],
0,
Math.max(...arr.map((item) => item.length)),
).filter((x) => x !== undefined);
};
console.log(transform(Array.from(map.values())));

最新更新