How to compare two array values and get the difference.
const list1 = ['1xx', '2bbb', '3ggg', '4eee'];
const list2 = ['1xx33', '2333', '3gfffgg', '4eeeooo'];
I'd like to check if list2 values contains list1s
so 1xx33 contains 1xx and 4eeeooo contains 4eee then the result I respect to see is ['2bbb', '3ggg'];
const output = list1.filter( function(n) { return !this.has(n) }, new Set(list2) );
Above snippet only gives matches but not contains
For every entry in the first list, you must verify if there is no matching substring in any of the values in the second list.
const output = list1.filter(s => list2.every(b => (b.indexOf(s)===-1)));
This snippet creates a new list by filtering the first list, removing every entry that has at least one matching substring when compared to the other list.
If you're not familiar with arrow functions (=>), here's the alternative:
const output = list1.filter(function(s) { return list2.every(function(b) { return (b.indexOf(s)===-1) })});
Related
I have project in which I need to develop a specific calculator. By far everything is good but now I am stuck in one problem. I have an array of the object containing letter as key and its value as below
valueList = [{a:5}, {b:3}, {c:8}, {d:6}]
and I have an input element where user can type specific characters like this
input = "a+b-c"
how do I modifie the above string to the new string that contains values of alphabets from valueList like
newVar = "5+3-8"
I have tried below solution with no far success
const final = input.split("").map((variable) => {
return valueList.forEach((element) => {
if (variable === Object.keys(element)[0]) {
return Object.values(element)[0];
} else {
return variable;
}
});
});
console.log(final);
First turn the valueList into an object with multiple properties, rather than an array of objects with single properties. Then use a regular expression to match any of the keys of the objects, and use a replacer function to look up the matching value on the object:
const valueList = [{a:5}, {b:3}, {c:8}, {d:6}];
const obj = Object.assign({}, ...valueList);
const input = "a+b-c";
const pattern = new RegExp(
Object.keys(obj).join('|'),
'g'
);
const output = input.replace(pattern, match => obj[match]);
console.log(output);
let destUpdate = this.state.destinations.filter((dest) => {
return !toRemove.includes(dest);
})
This is the code I have. Usually, it works as I want but I have a specific example where it does a weird thing.
In the example, this.state.destinations contain contains only one element, and toRemove is an empty array.
Since toRemove is empty, the filter will return true and should add dest to the new array but the returned array from filter (destUpdate) is empty.
How can this be?
Also, if I write the code like this:
let destUpdate = [];
this.state.destinations.forEach((dest) => {
if (!toRemove.includes(dest))
destUpdate.push(dest);
});
destUpdate is still empty (In the example where toRemove is empty and this.state.destinations contains contains only one element)
update:
toRemove: []
this.state.destinations: [{"lat":51.4862408,"lng":-0.1601986,"name":"National Army Museum","place_id":"ChIJma2VQBEFdkgRfxsd5E7cC3c","open":null,"timeSpent":1,"types":["tourist_attraction","museum","point_of_interest","establishment"],"assignedTime":"14:00 Friday","date":"10/4/2020"}]
Also, this code prints a non-empty array(as I want):
console.log(this.state.destinations.filter((dest) => {
return !toRemove.includes(dest);
}))
i have an array like [x/0/2 , x/0/3 , y/3/1 , x/1/1 , x/0/3 , x/1/2],
i need to convert the elements range like [x/0/2-3 , y/3/1 , x/1/1-2]
Please give some suggestion for this.
Use reduce to iterate over the array and create an object grouped by the element root, then use Object.entries to pull out the correct information from the object.
const arr = ['x/0/2', 'x/0/3', 'y/3/1', 'x/1/1', 'x/0/3', 'x/1/2'];
const out = arr.reduce((acc, c) => {
// `split` out the separate parts of the element
const [ root1, root2, index ] = c.split('/');
// We'll use the first two parts as the object key
const key = `${root1}/${root2}`;
// If the key doesn't already exist create an empty
// array as its values
acc[key] = acc[key] || [];
// To prevent duplicates only add an index if it
// isn't already in the array
if (!acc[key].includes(index)) acc[key].push(index);
// Return the accumulator for the next iteration
return acc;
}, {});
// Then iterate over the object entries with `map`
const result = Object.entries(out).map(([ key, values ]) => {
// Return the joined up value
return `${key}/${values.join('-')}`;
});
console.log(result);
If I understand your question, you could create an array within the array to hold the range of values. Checking if the position in the array is an actual array let’s you know there are values that span a range within.
Example:
var values = [x/01, [x/20, x/21, x/22], x/03]
You could also create an object that could accomplish something similar depending on your needs.
Sorry for the maybe misleading title.
I have two arrays, one contains the defaults and another one contains products.
What I am trying to do is compare the two so that you can add/remove as many products as you like, but you can't have less products as the default.
Lets say
default = [1,2]
products = [1,1,1,1,2,2,2,3,4,5,6,7,8,9]
this should work.
But you can't have something like this:
default = [1,2]
products = [2,2,2,3,4,5,6,7,8,9]
because at least the same amount of products in the default array is required, and in the last example, 1 is not included in the products array.
I am using this to compare the two arrays:
Array.prototype.containsArray = function ( array /*, index, last*/ ) {
if( arguments[1] ) {
var index = arguments[1], last = arguments[2];
} else {
var index = 0, last = 0; this.sort(); array.sort();
};
return index == array.length
|| ( last = this.indexOf( array[index], last ) ) > -1
&& this.containsArray( array, ++index, ++last );
};
arr1.containsArray( arr2 )
which works. In my function (the one used to add/remove products) I tried to have the check like this:
removeDeviceToZone = function(zone, ID) {
if (products.containsArray(default) {
return products = removeFromArray(products, ID);
}
};
but the problem is that at the time the check is executed, the array is still correct, but it won't be anymore as soon as a product is removed. What's the best way to have the check prevent what the array will be after removing the item without really removing it yet? Is it even possible? is it the best approach to do this? thanks
You should use every function which accepts a callback provided method applied on every item in the array.
The every() method tests whether all elements in the array pass the
test implemented by the provided function.
function containsArray(defaultArray, products){
return defaultArray.every(function(item){
return products.indexOf(item)!=-1;
});
}
let defaultArray = [1,2]
let products = [1,1,1,1,2,2,2,3,4,5,6,7,8,9];
let products2=[2,2,2,3,4,5,6,7,8,9];
let contains=containsArray(defaultArray,products);
let contains2=containsArray(defaultArray,products2);
console.log(products.toString()+'->'+contains);
console.log(products2.toString()+'->'+contains2);
When you delete items you should check if the containsArray keeps to be true. In the other words you have to check if the containsArray function returns true after remove element.If yes, return products. Otherwise, return the old products array.
removeDeviceToZone = function(zone, ID) {
let productsCopy=products;
let products=removeFromArray(products, ID);
if (containsArray(default,products) && containsArray(default,productsCopy) {
return products;
}
else
return productsCopy;
};
Simply putting,
Clone the original array in another variable (clonedArray) and compare the two after you are done deleting.
:)
Simplify the logic:
delete whatever you're going to delete
add the default values back in after each delete operation, e.g.:
for each default, check if it's already in the array and push if not
optionally sort the result if order matters to you
That's a simple, idempotent operation.
Alternatively, create a class that has the defaults as a property and the user-selected items as a separate property, and which merges both together conditionally as needed whenever necessary, e.g. basket.getMergedBasket().
Alternatively, instead of trying to maintain two lists, make those products objects if they aren't already which have an appropriate flag, e.g.:
products = [{ id: 1, mandatory: true }, ...]
That would be a really simple object oriented approach.
I want to sort an array by a split part of an array.
example_array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"]
I want to so it sorts it like this:
console.log(code here) // prints ["Zebra:Add","Jam:Back","Pay:Cold","And:Vets"]
Note: I want "Zebra:Add","Pay:Cold", etc to stay together. I just want it be sorted by the text after the ":".
From your comment on the question:
I can't even think of a solution
Break the problem into smaller pieces. You want to sort an array by a part of the strings in the array, so you need to figure out / look into
How to sort an array (you've done that, you've found the sort method)
How to isolate the part of the string you want to sort on
How to correctly compare strings for Array#sort
How to do #2 and #3 within the context of doing #1
Re #2, there are various ways to do that. You could find the : via String#indexOf and then use substring to get all characters after it. You could split the string on :, then use the second half (if you know there won't be more than one : in the string). Or you could use a regular expression to isolate everything after the first :.
For instance, someString.match(/:.*$/)[0] isolates all characters starting with the first :. (Including the : is harmless, but you could use .substring(1) if you don't want to include it.)
Re #3: Array#sort expects its callback to return a negative number if the first argument should come before the second, 0 if their order doesn't matter, or a positive number if the second should come before the first. String#localeCompare compares strings according to the current locale and returns exactly that information, so we want to use that.
Re #4: Array#sort accepts a callback function, so you could do all the string splitting and comparison in that callback. But since the callback will be called repeatedly, frequently with either the first or second argument being one that's already been checked before, for larger arrays doing it then may be inefficient. It may make more sense to do all the string splitting / isolation in advance, then do the sort, then get your desired result.
So:
The not-particularly-efficient way (which is fine for data sets like your small array) is to isolate the part you want to sort on within the sort callback:
var array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"];
array.sort(function(left, right) {
return left.match(/:.*$/)[0].localeCompare(right.match(/:.*$/)[0]);
});
var array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"];
array.sort(function(left, right) {
return left.match(/:.*$/)[0].localeCompare(right.match(/:.*$/)[0]);
});
console.log(array);
With ES2015+ syntax:
const array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"];
array.sort((left, right) =>
left.match(/:.*$/)[0].localeCompare(right.match(/:.*$/)[0])
);
const array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"];
array.sort((left, right) =>
left.match(/:.*$/)[0].localeCompare(right.match(/:.*$/)[0])
);
console.log(array);
If it's a massive array where doing those splits on every compare is problematic, you could map first, then sort, then unmap:
var array = /*...really big array...*/;
array =
array.map(function(entry) { return {full: entry, key: entry.match(/:.*$/)[0]};})
.sort(function(left, right) { return left.key.localeCompare(right.key); })
.map(function(entry) { return entry.full; });
var array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"];
array =
array.map(function(entry) { return {full: entry, key: entry.match(/:.*$/)[0]};})
.sort(function(left, right) { return left.key.localeCompare(right.key); })
.map(function(entry) { return entry.full; });
console.log(array);
With ES2015+ syntax:
let array = /*...really big array...*/;
array =
array.map(entry => ({full: entry, key: entry.match(/:.*$/)[0] }))
.sort((left, right) => left.key.localeCompare(right.key))
.map(entry => entry.full);
let array = ["Zebra:Add","Pay:Cold","And:Vets","Jam:Back"];
array =
array.map(entry => ({full: entry, key: entry.match(/:.*$/)[0] }))
.sort((left, right) => left.key.localeCompare(right.key))
.map(entry => entry.full);
console.log(array);
I like the simplicity of the previous answer, in comparison My approach is probably too wordy! But here goes...
1.) take the original array and build a new sorting array from it, JSON array with each object having a text1 and text2 value... we'll sort on the text 2 value
2.) run a sort based on the text2 value
3.) empty the original array
4.) loop over the sorting array and re-populate the original array
heres a fiddle example I threw together
// STARTING ARRAY. WE WANT TO SORT BY THE TEXT AFTER THE COLON
example_array = ["Zebra:Add", "Pay:Cold", "And:Vets", "Jam:Back"];
// AN EMPTY ARRAY TO BUILD A JSON ARRAY FROM, THE SORT FROM THE DESIRED TEXT STRING
sorting_array = [];
// LOOP THROUGH THE ORIGINAL ARRAY AND PUSH A NEW OBJECT TO THE SORTING ARRAY
// EACH OBJECT CONTAINS A TEXT1 VALUE AND A TEXT2 VALUE
$.each(example_array, function(i, val){
sorting_array.push({"text1": val.split(':')[0], "text2": val.split(':')[1]})
})
// SORT THE SORTING ARRAY BY THE TEXT2 VALUE
sorting_array.sort(function(a, b){
if (a.text2 < b.text2) return -1;
if (b.text2 < a.text2) return 1;
return 0;
});
// EMPTY OUR ORIGINAL ARRAY
example_array = [];
// FOR DEMO PURPOSES LETS DISPLAY EACH IN THE DOM IN A UL ,
// AND ALSO RE-POPULATE THE ORIGINAL ARRAY WITHT HE NEW ORDER
$.each(sorting_array, function(i, val){
example_array.push(val.text1+':'+val.text2)
})
// TO SHOW THE NEW ORDER, LETS LOOP BACK OVER THE EXAMPLE_ARRAY
$.each(example_array, function(i, val){
$('ul').append('<li>' + val+ '</li>');
})