why is this removeDuplicate not working? - javascript

I making a function that simply removes duplicate strings, so there are only unique strings left:
var d = ['a','b','B','C','d','e','f','e'];
d.length; //8
function removeDuplicates(data) {
var i, j, a, b;
for(i = 0; i < data.length - 1; i++) {
a = data[i];
for(j = i; j < data.length; j++) {
b = data[j];
if (a.toLowerCase() == b.toLowerCase()) {
data.splice(j, 1);
}
}
}
}
removeDuplicates(d)
d.length; //4 ????
There were only two duplicates, Only B, e should have gotten removed.
but I get this:
["b", "C", "e", "e"]

Your issue is coming from the fact that any time i === j you'll have a match, and it'll be removed. In your inner loop, just put a check so that you only do your "remove them if they're equal" in situations where i !== j
Also, as nnn noted in the comments, splicing will mess up the index. The cleanest way to do this is with a filter and an every.
Updated solution below:
var d = ['a', 'b', 'B', 'C', 'd', 'e', 'f', 'e'];
var e = ['e','e','e','e'];
// d.length; //8
function removeDuplicates(data) {
const noDuplicates = data.filter((letter, ogIndex) => {
return data.every((innerLetter, innerIndex) => {
if (ogIndex < innerIndex) {
const check = letter.toLowerCase() !== innerLetter.toLowerCase();
return check;
} else {
return true;
}
})
})
return noDuplicates
}
console.log(removeDuplicates(d));
// ["a", "B", "C", "d", "f", "e"]
console.log(removeDuplicates(e));
// ["e"]

With ES6/ES2015, a simple way of getting a list of unique items is to use the new Set data type.
To "unique"-ify an array, just convert it to a set and then back to an array. i.e.
[...new Set(originalArray)]
(Update: My answer originally used Array.from(S_E_T) to convert the set back to an array. I have now changed it to [...S_E_T] to do that conversion.)
To do the same in a case-insensitive manner, follow the same logic, but just convert each original array element to its lowercase equivalent before converting that whole array to the set.
To do keep only the first instance of a string of any case (e.g. to keep 'cAt' from ['cAt', 'CaT'] instead of just the all-lowercased 'cat'), first perform the case-insensitive search as above, then get the index of each unique lowercase element from the lowercased un-uniqified original array, then use that index to retrieve the whichever-cased element from the original-cased un-uniqified original array.
const show = msg => {console.log(JSON.stringify(msg))};
const f = (arr) => {
const arrLC = arr.map(x => x.toLowerCase(x));
return [...new Set(arrLC)].map(x => arr[arrLC.indexOf(x)])
};
const d1 = ['a','b','B','C','d','e','f','e'];
const d2 = ['e', 'e', 'e', 'e', 'e'];
show(d1);
show([...new Set(d1)]);
show([...new Set(d1.map(x => x.toLowerCase(x)))]);
show(f(d1));
show('------');
show(d2);
show([...new Set(d2)]);

Related

What does need to be corrected for my solution in order for it to work properly

UPDATE: I figured out the solution on my own. instead of doing the solutions below, a simpler solution is
CODE SOURCE
let trUp = (chord)=>{
let index= arr.indexOf(chord);
return arr.IndedOf(index) - 1;
}
You could use an array and look for the index and increment by adjusting by the length of the array with the reminder operator %.
function up(note) {
return notes[(notes.indexOf(note) + 1) % notes.length];
}
var notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'];
console.log(up('C'));
Slightly longer version that would allow you to transpose single chords or an array of chords either up / down...
const chordMap = ['A','A#','B','C','C#','D','D#','E','F','F#','G','G#'];
Define some functions to move chords either up or down 1 and return the result
function chordUp(chord) {
let index = chordMap.indexOf(chord) + 1;
if (index >= chordMap.length) index = 0;
return chordMap[index];
}
function chordDown(chord) {
let index = chordMap.indexOf(chord) - 1;
if (index < 0) index = chordMap.length - 1;
return chordMap[index];
}
Then functions to transpose an entire array of chords up or down 1 and return the results as an array
function transposeUp(input) {
let output = [];
for (let i = 0; i < input.length; i++) {
let chord = input[i];
output.push(chordUp(chord));
}
return output;
}
function transposeDown(input) {
let output = [];
for (let i = 0; i < input.length; i++) {
let chord = input[i];
output.push(chordDown(chord));
}
return output;
}
Finally you can use it like so...
let chords = ['A', 'G#', 'B', 'C#', 'A#', 'G'];
let transposedUp = transposeUp(chords); // [ "A#", "A", "C", "D", "B", "G#" ];
let transposedDown = transposeDown(chords) // [ "G#", "G", "A#", "C", "A", "F#" ];

breaking from a for look inside a reucrsive function

Please see the fiddle.
I am bit stuck with why the loop does not break the way I intended.
input is an object, indexed with some kind of id. And the values are the children ids. so, I am trying to filter out (here it is hard coded as d) a node and its children. For example, for d, there is only one child, h. so, I was expecting an output ['d', 'h']. This is because, once the filter condition met (c === 'd'), it discards all previous value, and just recurse to its children (only h) in this case. Once the recursion to children finished, the break should terminate the for loop. but as I see from the output, the for loop doesn't breaks and continues with (c -> g). How to make this so once the filter met, it only output the node and all its children ['d', 'h'] in this case.
const input = {
a: ['b', 'c'],
b: ['d', 'e', 'f'],
c: ['g'],
d: ['h'],
}
let data = [];
const recur = (d) => {
const ch = input[d]
if (!ch) {
return;
}
for (let i = 0; i < ch.length; i++) {
console.log(data)
const c = ch[i]
data.push(c)
if (c === 'd') {
data = [c];
console.log("reset data", data)
recur(c)
break
}
recur(c)
}
}
recur('a')
console.log("end: ", data)
output: end: ["d", "h", "c", "g"]
This is what I came up with. https://jsfiddle.net/j6s1vouv/4/ There need a way to inform the parent that, the filter is met and should skip the sibling iteration.
const input = {
a: ['b', 'c'],
b: ['d', 'e', 'f'],
c: ['g'],
d: ['h'],
}
const recur = (d) => {
const ch = input[d]
if (!ch) {
return {
data: []
};
}
let out = [];
for (let i = 0; i < ch.length; i++) {
const c = ch[i]
const x = recur(c)
if (x.skip) { // if any of the child met filter
return x
}
if (c === 'd') { // current id met filter
return {
data: [c].concat(x.data),
skip: true
}
}
out = out.concat([c], x.data)
}
return {
data: out
}
}
console.log("end: ", recur('a').data)

Partial string matching in javascript

I have this javascript function:
var y;
y = ['ab', 'f', 'c'].indexOf('b')
This returns -1 because b is not a full match against ab
In javascript, what is the easiest way of looking for a partial match.
So I want :
y = ['ab', 'f', 'c'].indexOf('b')
to return 0(index of first element because it found a partial match while comparing b to ab
I understand i cannot do it using indexOf, what is the best way to do it?
You will have to loop through the Array:
var arr = ['ab', 'f', 'c']
function find(a, c) {
for(var i = 0; i < a.length; ++i) {
if(a[i].indexOf(c) != -1)
return i
}
return -1
}
// Example:
document.body.innerHTML = find(arr, "b")
With ES6 Array.prototype.findIndex and arrow functions:
y = ['ab', 'f', 'c'];
y.findIndex(el => el.indexOf('b') > -1); //0
You have multiple way, you can use the method Array.prototype.filter:
var testRegex = /b/;
var result = ['ab','f','c'].map(function(item, index) {
return testRegex.test(item) ? index : null; //return index in the array if found.
}).filter(function(index) {
return index !== null; // filter the result and remove all results that are null;
});
result should equal [0];
if you try with an array like this:
['ab', 'cgt', 'abcd', 'cd', 'efb'];
It should return:
[0, 2, 4]

Javascript arrays ab=ba

If i have a multidimensional array like: [[a,b],[a,c],[b,a],[b,c],[c,a],[c,b]] how can i go through and remove repeats where [a,b] is the same as [b,a].
also, the array is actually massive, in the tens of thousands. A for loop would have to be done backwards because the array length will shrink on every iteration. Im not even sure that an each loop would work for this. I really am at a loss for just a concept on how to begin.
Also, i tried searching for this for about an hour, and i don't even know how to phrase it.
I think I'm going to try a different approach to this problem. I also think it'll be quicker than some of the solutions proposed (though we'd need of course to test it and benchmark it).
First off, why don't we take advantage of the hash oriented nature of javascript arrays and objects? We could create an object containing the relations (in order to create a kind of a map) and store in a new array those relationships that hasn't been stored yet. With this approach there's no problem about objects either, we just request for an identifier or hash or whatever for every object. This identifier must make the relationship between them possible.
UPDATE
The script now controls the possibility of repeated elements f.e [[a,b],[a,b]]
The script now controls the possibility of elements with the same object repeated f.e [[a,a],[a,a][a,a]] would return [a,a]
The code:
var temp = {},
massive_arr = [['a','b'],['a','c'],['a','d'], ['b','a'],['b','c'],['b','d'],['c','a'],['c','b'],['c','d']],
final_arr = [],
i = 0,
id1,
id2;
for( ; i < massive_arr.length; i++ ) {
id0 = objectIdentifier(massive_arr[i][0]);// Identifier of first object
id1 = objectIdentifier(massive_arr[i][1]);// Identifier of second object
if(!temp[id0]) {// If the attribute doesn't exist in the temporary object, we create it.
temp[id0] = {};
temp[id0][id1] = 1;
} else {// if it exists, we add the new key.
temp[id0][id1] = 1;
}
if( id0 === id1 && !temp[id0][id1+"_bis"] ) {// Especial case [a,a]
temp[id0][id1+"_bis"] = 1;
final_arr.push(massive_arr[i]);
continue;// Jump to next iteration
}
if (!temp[id1]) {// Store element and mark it as stored.
temp[id1] = {};
temp[id1][id0] = 1;
final_arr.push(massive_arr[i]);
continue;// Jump to next iteration
}
if (!temp[id1][id0]) {// Store element and mark it as stored.
temp[id1][id0] = 1;
final_arr.push(massive_arr[i]);
}
}
console.log(final_arr);
function objectIdentifier(obj) {
return obj;// You must return a valid identifier for the object. For instance, obj.id or obj.hashMap... whatever that identifies it unequivocally.
}
You can test it here
SECOND UPDATE
Though this is not what was requested in the first place, I've changed the method a bit to adapt it to elements of n length (n can vary if desired).
This method is slower due to the fact that relies on sort to generate a valid key for the map. Even so, I think it's fast enough.
var temp = {},
massive_arr = [
['a', 'a', 'a'], //0
['a', 'a', 'b'], //1
['a', 'b', 'a'],
['a', 'a', 'b'],
['a', 'c', 'b'], //2
['a', 'c', 'd'], //3
['b', 'b', 'c'], //4
['b', 'b', 'b'], //5
['b', 'b', 'b'],
['b', 'c', 'b'],
['b', 'c', 'd'], //6
['b', 'd', 'a'], //7
['c', 'd', 'b'],
['c', 'a', 'c'], //8
['c', 'c', 'a'],
['c', 'd', 'a', 'j'], // 9
['c', 'd', 'a', 'j', 'k'], // 10
['c', 'd', 'a', 'o'], //11
['c', 'd', 'a']
],
final_arr = [],
i = 0,
j,
ord,
key;
for (; i < massive_arr.length; i++) {
ord = [];
for (j = 0; j < massive_arr[i].length; j++) {
ord.push(objectIdentifier(massive_arr[i][j]));
}
ord.sort();
key = ord.toString();
if (!temp[key]) {
temp[key] = 1;
final_arr.push(massive_arr[i]);
}
}
console.log(final_arr);
function objectIdentifier(obj) {
return obj;
}
It can be tested here
Based on my understanding that you want to remove from the parent array any children arrays which hold the same set of objects without regard for order, this should do it is some code:
function getId(obj) { // apparently these objects have identifiers
return obj._id; // I'm testing with MongoDB documents
}
function arraysEqual(a, b) {
if (a === b) { return true; }
if (a == null || b == null) { return false; }
if (a.length != b.length) { return false; }
aIds = []; bIds = [];
for (var i = 0; i < a.length; i++) {
aIds.push(getId(a[i])); bIds.push(getId(b[i]));
}
aIds.sort(); bIds.sort();
for ( var i = 0; i < aIds.length; i++ ) {
if(aIds[i] !== bIds[i]) { return false; }
}
return true;
}
function removeRepeats(list) {
var i, j;
for (i=0; i < list.length; i++) {
for (j=i+1; j < list.length; j++) {
if (arraysEqual(list[i], list[j])) {
list.splice(j,1);
}
}
}
}
The removeRepeats function goes through each element and compares it with every element that comes after it. The arraysEqual function simply returns true if the arrays are equal. The isEquivalent function should test object equivalence. As noted on that webpage, there are libraries that test object equivalence. If you are okay with adding those libraries, you can replace the isEquivalent function with _.isEqual.
***
* Turns out the OP has objects in his list, so this approach won't
* work in that case. I'll leave this for future reference.
***
var foo = [['a','b'],['a','c'],['b','a'],['b','c'],['c','a'],['c','b']];
function removeRepeats(list) {
var i;
var b = [];
var _c = [];
for (i = 0; i < list.length; i++) {
var a = list[i].sort();
var stra = a.join("-");
if(_c.indexOf(stra) === -1) {
b.push(a);
_c.push(stra);
}
}
return b;
}
console.log(removeRepeats(foo));
It's not the most pretty code I've ever produced, but it should be enough to get you started I guess. What I'm doing is creating two new arrays, b and _c. b will be the array without the repeats. _c is a helper array which contains all the unique pairs already processed as a string, so I can do easy string comparisons while looping through list.

Reverse part of an array using javaScript

I have an array in Javascript like so:
var v = new Array("a","b","c","d","e","f"...);
I would like to reverse it but keep the first two elements, so it would turn into:
"a","b",...."f","e","d","c"
How can I do this?
Try this way:
var v = new Array("a","b","c","d","e","f");
var newArr = v.splice(0,2).concat(v.reverse()); // get the first 2 out of the array
console.log(newArr);
splice
concat
reverse
v = [].concat( v.slice(0,2), v.slice(2).reverse());
//v --> ["a", "b", "f", "e", "d", "c"]
function reverseArrayFromThirdElement(array, copy) {
var arrayCopy = copy ? array.concat() : array;
return [array[0], array[1]].concat(
(arrayCopy.splice(0, 2), arrayCopy.reverse())
);
}
It allows you to choose if you want to keep your array safe from slicing. Pass copy as true if you want your array to be internally copied.
It works like Array.Reverse from C#:
const arrayReverse = (arr, index, length) => {
let temp = arr.slice(index, index + length);
temp.reverse();
for (let i = 0, j = index; i < temp.length, j < index + length; i++, j++) {
arr[j] = temp[i];
}
return arr;
};
Example:
let test = ['a', 'b', 'c', 'd', 'e', 'f'];
test = arrayReverse(test, 4, 2);
console.log(test); // returns [ 'a', 'b', 'c', 'd', 'f', 'e' ]
For inplace reversal, this can be done using splice method:
let alp = new Array("a","b","c","d","e","f");
alp.splice(2,4,...alp.slice(2).reverse());
console.log(alp);
Use destructuring:
const reversedEnd = [...v.slice(0, 2), ...v.slice(2).reverse()];
const reversedBeginning = [...v.slice(0, 2).reverse(), ...v.slice(2)];
const reversedMiddle = [...v.slice(0, 2), ...v.slice(2,4).reverse(), ...v.slice(4)];

Categories