javascript unique string array case insensitive but keep one case sensitive result

风流意气都作罢 提交于 2020-01-25 07:49:07

问题


What do I mean with this? First let's look at some code I wrote:

  let names = ['James', 'james', 'bob', 'JaMeS', 'Bob'];
  let uNames = {};

  names.forEach(n => {
    let lower = n.toLowerCase();
    if (!uNames[lower]) {
      uNames[lower] = n;
    }
  });

  names = Object.values(uNames);
  console.log(names); // >>> (2) ["James", "bob"]

The goal here is to unique the given array case insensitive but keep one of the original inputs.

I was wondering if there is a more elegant/better performing solution to this problem than the one I came up with.

Just converting the whole array to lowercase before making it unique is not a solution, because I'd like the end result to consist only of values which were already in the input array. Which one (e.g. James or james or JaMeS) is not relevant.


回答1:


I was wondering if there is a more elegant/better performing solution to this problem than the one I came up with.

Use a Map:

let names = ['James', 'james', 'bob', 'JaMeS', 'Bob'];
let uNames = new Map(names.map(s => [s.toLowerCase(), s]));

console.log([...uNames.values()]); 

The constructor of Map can take an array of pairs (nested arrays with 2 values: key and value). The Map will maintain a unique list of keys so while it is constructed previously stored values will get overwritten if the key is the same.

Once you have the Map, you can iterate over the values with .values().

With plain object

You could also use the Object.fromEntries method, which at the time of writing, is a stage 4 proposal (Draft ES2020) implemented in Chrome, Firefox, Opera and Safari:

let names = ['James', 'james', 'bob', 'JaMeS', 'Bob'];
let uNames = Object.fromEntries(names.map(s => [s.toLowerCase(), s]));

console.log(Object.values(uNames)); 

As you can see, the approach is quite similar.

First occurrence & Original order

The above will collect the last occurrence, in order of first occurrence.

In case you want to collect the first occurrence, you can just reverse the input first, and then continue as above. Then the output will have collected the first occurrence, in order of last occurrence.

In case you really need the first occurrences in order of first occurrence, you can use reduce as follows:

let names = ['James', 'james', 'bob', 'JaMeS', 'Bob'];
let uNames = names.map(s => s.toLowerCase()).reduce((map, s, i) => 
    map.get(s) ? map : map.set(s, names[i])
, new Map);

console.log([...uNames.values()]); 



回答2:


If you want to deduplicate a string array, with priority given to the first occurrences, keeping the insertion order, use this.

const a = ["ALPHA", "10", "BETA", "10", "alpha", "beta", "aLphA"];
const b = ["3", "1", "2", "2", "3", "1"];

function dedupe(string_array) {
	const entries = string_array
		.slice()
		.reverse()
		.map((string, index) => [
			string.toLowerCase(),
			{
				string,
				index: string_array.length - 1 - index
			}
		]);
	const case_insensitively_deduped_string_array = Array
		.from((new Map(entries)).values())
		.sort((a, b) => (a.index - b.index))
		.map(item => item.string);
	return case_insensitively_deduped_string_array;
	// Takes the first occurrences, keeping the insertion order.
	// Doesn’t modify the input array.
}

console.log(a, dedupe(a));
console.log(b, dedupe(b));


来源:https://stackoverflow.com/questions/48731396/javascript-unique-string-array-case-insensitive-but-keep-one-case-sensitive-resu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!