Delete the array items which have lower specific value - javascript

I want to let array1 transform to array2.
The keyword is the test,test2,885,length.I want the keyword's next value(#?) to the next # is the highest.
var array1=["4#test#4#T#limited","6#test#6#885#restricted","7#test2#2#2#limited","8#test2#4#3#limited","11#885#1#TT#restricted","15#length#1#taw#restricted","17#885#11#T#limited"];
var arrar2=["6#test#6#885#restricted","8#test2#4#3#limited","17#885#11#T#limited","15#length#1#taw#restricted"];

Is this what you want?
var array1=["4#test#4#T#limited","6#test#6#885#restricted","7#test2#2#2#limited","8#test2#4#3#limited","11#885#1#TT#restricted","15#length#1#taw#restricted","17#885#11#T#limited"];
var keywords = ["test","test2","885","length"];
var array2 = [];
keywords.forEach(function (key) {
var matched = [];
var regex = new RegExp("\\b" + key + "#(\\d+)");
array1.forEach(function (value) {
if (regex.test(value)) {
matched.push([parseInt(RegExp.$1), value]);
}
});
if (matched.length > 0) {
matched.sort(function (a, b) {
return b[0] - a[0];
});
array2.push(matched[0][1]);
}
});
alert(array2);
JsFiddle example

You can use underscorejs to filter.
http://underscorejs.org/#filter
Then you can use javascript Array's sort method to sort the array if needed. You can pass a custom sorting function to sort. http://www.w3schools.com/jsref/jsref_sort.asp

The simplest way to "delete items from an array" based on the items fulfilling a condition is to use Array.prototype.filter (with the condition reversed).
In your case, it's rather hard to do because of how you organized your data (bundling it all in a string makes it hard to work with). Here is how i would work around it, but feel free to structure your data better from the start to avoid the awkward conversions:
var array1=["4#test#4#T#limited","6#test#6#885#restricted","7#test2#2#2#limited","8#test2#4#3#limited","11#885#1#TT#restricted","15#length#1#taw#restricted","17#885#11#T#limited"];
Make the data easier to work with by extracting the keyword and the value (i also keep some information to return to the original shape of the array):
var saneData = array1.map(function(item, index){
var split = item.split('#');
return {
keyword : split[1],
value: +split[0],
original : item,
originalIndex : index
}
});
Sort the data by the keyword (so all items with the same keywords go next to each other) and by the values (so the biggest value is first in a subset with the same keywords):
saneData.sort(function(a,b){
if (a.keyword !== b.keyword) {
return a.keyword.localeCompare(b.keyword);
} else {
return b.value - a.value;
}
});
Filter the array, keeping just the first item in a subset with the same keyword:
var filteredData = saneData.filter(function(item, index) {
if (index === 0) return true; // first item is good
return item.keyword !== saneData[index-1].keyword;
});
Return to the original form of the array (with the order preserved):
var array2 = filteredData.sort(function(a,b){ return a.originalIndex - b.originalIndex; }).map(function(item){ return item.original; });
// Result: ["6#test#6#885#restricted", "8#test2#4#3#limited", "15#length#1#taw#restricted", "17#885#11#T#limited"]

Related

Using Map-Reduce to Return A Modified Array of Strings

I am learning map & reduce, but am having a hard time understanding how to utilize these methods to tackle problems.
For example,
Create a function that takes a number and returns an array of strings containing the number cut off at each digit.
420 should return ["4", "42", "420"]
My old Approach:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
var output = [];
for(var i = numArr.length-1; i>=0; i--) {
output.unshift(numArr.join('');
numArr.pop();
}
return output;
}
Attempt to use map-reduce combination:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
return numArr.map(function(element) {
var newElement = numArr.reduce(function(acc, val) {
return acc + val;
});
numArr.splice(element, 1);
return newElement;
});
}
You have used two loops, but apparently it can be done just with one.
function n(num) {
let res = (""+num).split('').map((_,i) => (""+num).slice(0, i+1));
return res;
}
console.log(n(420));
console.log(n(13579));
One-liner.
const n = num => (""+num).split('').map((_,i) => (""+num).slice(0, i+1));
console.log(n(420));
console.log(n(13579));
As others noted, that this problem doesn't seem to be the best use case of the map and reduce functions.
map function provides the element, index and array information in the parameters. Making use of these you can iterate on the elements you need to apply the reduce function.
Statement var arrayToIterate = arr.slice(0,i+1); helps to achieve the above mentioned array to iterate.
Complete Code:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
return numArr.map(function(element, i, arr) {
var arrayToIterate = arr.slice(0,i+1);
var newElement = arrayToIterate.reduce(function(acc, val) {
return acc + val;
},"");
return newElement;
});
}
var result = createArrayOfTiers(420);
console.log(result);
I don't think these are good uses-cases of map or reduce, but here goes :
var numArr = [4,2,0];
var result = numArr.map(e => numArr.join('')) // now ["420", "420", "420"]
.map((arr, i) => arr.substring(0, i+1)) // now ["4", "42", "420"]
console.log(result);
We first replace every element of the array by the whole (unmodified) array joined into a string, then substring each of these strings based on their position in the outer array.
There's a reduction hidden in there, although not using reduce : join reduces [4, 2, 0] to "420".
I am learning map & reduce, but am having a hard time understanding how to utilize these methods to tackle problems.
Mapping associates to each value of the source array a new value provided by a mapping function : if you have an [x, y, z] array, a mapping function f(x)=x+1, the result of mapping the array with the function will be [x+1, y+1, z+1].
I believe reduction was meant to "reduce" an array to a primitive type, although I might be mistaken. If you have an [x, y, z] array and reduce it with the addition operation, the result will be x+y+z.

Javascript sorting input values into an array

I have the below code which looks at hidden inputs with the class howmanyproducts.
$("#proddiv .howmanyproducts").each(function() {
var idval = $(this.id).val();
});
What I am trying to achieve is for each id get the value and sort the ids into an array based on their values.
You have to add both your ids and the corresponding values to an array, then to use the sort method of your array with an adapted compare function. Let say your values are number but are retrieved as string :
// Create a new empty array :
var ids = [];
$("#proddiv .howmanyproducts").each(function() {
var id = this.id;
var val = $(this.id).val();
// Store the id and the value:
ids.push([id, +val]);
});
// Sort the array in place, using the second element of each item
// ie. the value :
ids.sort(function(a, b) { return a[1] - b[1]; });
// Once sorted, make a new array with only the ids:
var result = ids.map(function(a,b) { return a[0] });
I used the traditional syntax, but note that it can be written more simply using arrow functions:
ids.sort((a,b) => a[1] - b[1]);
var result = ids.map((a,b) => a[0]);
var arrayOfValues = [];
$("#proddiv .howmanyproducts").each(function() {
arrayOfValues.push($(this).val());
}

Return the index and array after reduced

I am playing with reduce method provided by mozilla. Underscore provides its own version which seems very similar. Reducing an array is very simple.
ArrayName.reduce(function(a,b) {
return a +b;
});
I use a very similar approach when sorting arrays as well. However, what I do not understand is how to return a list of calls. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce Provides a really cool table that shows how their method works. I set my array pretty much the same
var array = [1,2,3,100,55,88,2];
var sortArray= function(a,b) {
return a -b;
}
var sorted = array.sort(sortArray);
var reduced = sorted.reduce(function(previousValue,currentValue, index,array){
return previousValue + currentValue;
});
What I wanted to set up though was each call that was made. I figured that I could simply reference the index in the return value with a , at the end.
return previousValue + currentValue, index;
However, that only returns the index. I am trying to figure a way to get the value of each call. Does this approach allow you to get each call?
You don't need to use reduce for what you are doing. Try
function running_total(array) {
var sum = 0;
return array.map(function(elt) {
return sum += elt;
};
}
Reduce is about "boiling down" an array to a "single thing". In contrast, map is about "transforming" elements of the array. Your problem (as I understand it) is to transform each element of the array into the sum of itself and all the preceding elements. Therefore, conceptually, it's a map, not a reduce.
Using the , in the return statement will only return index due to the comma operator rule
[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) {
console.log(previousValue+"--"+currentValue)
return previousValue + currentValue;
});
this will do want you want
You can create another array and store its value for each progress.
var array = [1,2,3,100,55,88,2];
var sortArray= function(a,b) {
return a -b;
}
var sorted = array.sort(sortArray);
var progress = [];
var reduced = sorted.reduce(function(previousValue,currentValue, index,array){
progress[index] = previousValue + currentValue;
return progress[index];
}, 0);
// ^^^^^
// give it a init value so it starts at first element.
console.log(progress);
However, as torazaburo says, as you expect to get an array, you should use .map which returns an array instead of .reduce.

Javascript: Find douplicated values from array with keys

Title is pretty much self explanatory...
I want to be able to find duplicated values from JavaScript array.
The array keys can be duplicated so I need to validate only the array values.
Here is an example :
var arr=[
Ibanez: 'JoeSatriani',
Ibanez: 'SteveVai',
Fender: 'YngwieMalmsteen',
Fender: 'EricJohnson',
Gibson: 'EricJohnson',
Takamine: 'SteveVai'
];
In that example:
the key is the guitar brand
the value is the guitar player name.
So:
If there is duplicated keys (like: Ibanez or Fender) as on that current example that is OK :-)
But
If there is duplicated values (like: EricJohnson or SteveVai) I'm expecting to get (return) that error:
EricJohnson,SteveVai
You can't have associative arrays in Javascript. You can create an array of objects, like:
var arr=[
{Ibanez: 'JoeSatriani'},
{Ibanez: 'SteveVai'},
{Fender: 'YngwieMalmsteen'},
{Fender: 'EricJohnson'},
{Gibson: 'EricJohnson'},
{Takamine: 'SteveVai'}
];
Then you'll need a for...in loop to go over every object in the array, create a new array of values and check that for duplicates, which is also not very straightforward - basically you'll want to sort the array and make sure no value is the same as the one after it.
var arrayOfValues = [];
arr.forEach(function(obj){
for(var prop in obj)
arrayOfValues.push(obj[prop]);
});
arrayOfValues.sort(); // by default it will sort them alphabetically
arrayOfValues.forEach(function(element,index,array){
if(array[index+1] && element==array[index+1])
alert("Duplicate value found!");
});
First of all, object keys can not be repeated.
This means that:
({
"Fender": "Jim",
"Fender": "Bob"
})["Fender"]
Would simply return: "Bob".
However, I did make a code that could allow you to find duplicates in values, but as I said, the key will have to be unique:
var arr = {
Ibanez: 'EricJohnson',
Fender: 'YngwieMalmsteen',
Gibson: 'EricJohnson',
Takamine: 'SteveVai',
"Takamine2": 'SteveVai'
};
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
var track = [];
var exists = [];
for (var val in arr) {
if (contains(track, arr[val])) {
exists.push(arr[val]);
} else {
track.push(arr[val])
}
}
alert(exists)
You can see it working here: http://jsfiddle.net/dr09sga6/2/
As others have commented, the example array you provided isn't a valid JavaScript array. You could, however, keep a list for each guitar type:
var mapping = {
Ibanez: ['JoeSatriani','SteveVai'],
Fender: ['YngwieMalmsteen','EricJohnson']
Gibson: ['EricJohnson'],
Takamine: ['SteveVai']
];
Or a list of each guitar/musician pair:
var pairs = [
['Ibanez','JoeSatriani'],
['Ibanez','SteveVai'],
['Fender','YngwieMalmsteen'],
['Fender','EricJohnson'],
['Gibson','EricJohnson'],
['Takamine','SteveVai']
];
Your solution is going to depend on which pattern you go with. However, in the second case it can be done in one chained functional call:
pairs.map(function(e) {return e[1]}) // Discard the brand names
.sort() // Sort by artist
.reduce(function(p,c,i,a){
if (i>0 && a[i]==a[i-1] && !p.some(function(v) {return v == c;})) p.push(c);
return p;
},[]); //Return the artist names that are duplicated
http://jsfiddle.net/mkurqmqd/1/
To break that reduce call down a bit, here's the callback again:
function(p,c,i,a){
if (i>0
&& a[i]==a[i-1]
&& !p.some(function(v) {
return v == c;
}))
p.push(c);
return p;
}
reduce is going to call our callback for each element in the array, and it's going to pass the returned value for each call into the next call as the first parameter (p). It's useful for accumulating a list as you move across an array.
Because we're looking back at the previous item, we need to make sure we don't go out of bounds on item 0.
Then we're checking to see if this item matches the previous one in the (sorted) list.
Then we're checking (with Array.prototype.some()) whether the value we've found is ALREADY in our list of duplicates...to avoid having duplicate duplicates!
If all of those checks pass, we add the name to our list of duplicate values.

Another javascript array challenge

Solving another array manipulation, and I'm taking longer than usual to solve this. I need help in combining array values:
var array1 = ["alpha|LJ", "bravo|MH", "charlie|MH", "delta|MF",
"echo|16", "{foxtrot}|GG", "{golf}|HS"];
var array2 = ["charlie-{golf}-{foxtrot}", "echo-{golf}"]; //some templates
such that the final array be:
final_array = ["alpha-LJ", "bravo-MH", "charlie-HS-GG-MH", "delta-MF",
"echo-HS-16"];
To make it clear how I arrived with the final_array, alpha, bravo and delta only got their "|" replaced with "-" since they are not found on my array2 template. charlie and echo got the template so the respective values of the {} were replaced based on array1. Array1 honestly is not the best key:value relationship that I could come up for now.
Here are some requirementL:
* Anything in array1 with {} braces are not meant to be templated.
* Keywords in array2 will always have a matching value in array1.
I've read about jquery .map() and thinking that it is achievable using this, maybe together with Regexp. Hope you'll utilize these. Also, if it helps, final_array can be of any order.
I really need to up my knowledge on these two topics... :|
Thank you in advance.
Edit: Updated to match your output and comment some of the madness. This doesn't feel like it's the most efficient, given the split() done to values at the start and then again at the end...but it works.
function funkyTransform( values, templates ){
// Make a copy of the array we were given so we can mutate it
// without rudely changing something passed to our function.
var result = values.concat();
// Map {value} entries for later lookup, and throw them out of the result
var valueMap = {};
for (var i=result.length-1;i>=0;--i){
var pair = result[i].split('|');
if (pair[0][0]=="{"){
valueMap[pair[0]] = pair[1];
result.splice(i,1); // Yank this from the result
}
}
console.log(valueMap);
// {
// "{foxtrot}": "GG",
// "{golf}": "HS"
// }
// Use the value map to replace text in our "templates", and
// create a map from the first part of the template to the rest.
// THIS SHOULD REALLY SCAN THE TEMPLATE FOR "{...}" PIECES
// AND LOOK THEM UP IN THE MAP; OOPS O(N^2)
var templateMap = {};
for (var i=templates.length-1;i>=0;--i){
var template = templates[i];
for (var name in valueMap){
if (valueMap.hasOwnProperty(name)){
template = template.replace(name,valueMap[name]);
}
}
var templateName = template.split('-')[0];
templateMap[ templateName ] = template.slice(templateName.length+1);
}
console.log(templateMap);
// {
// "charlie": "HS-GG",
// "echo": "HS"
// }
// Go through the results again, replacing template text from the templateMap
for (var i=result.length-1;i>=0;--i){
var pieces = result[i].split('|');
var template = templateMap[pieces[0]];
if (template) pieces.splice(1,0,template);
result[i] = pieces.join('-');
}
return result;
}
var output = funkyTransform( array1, array2 );
console.log(output);
// ["alpha-LJ", "bravo-MH", "charlie-HS-GG-MH", "delta-MF", "echo-HS-16"]
This managed to get your desired output, though I made a few assumptions:
Anything in array1 with {} braces are not meant to be templated.
Keywords in array2 will always have a matching value in array1 (this can easily be changed, but not sure what your rule would be).
Code:
// This is the main code
var final_array = $.map(array1, function (item) {
var components = item.split('|');
// Ignore elements between {} braces
if (/^\{.*\}$/.test(components[0])) return;
components[0] = template(components[0]);
return components.join('-');
});
// Helper to lookup array2 for a particular string and template it
// with the values from array1
function template(str) {
var index = indexOfMatching(array2, str, '-');
if (index == -1) return str;
var components = array2[index].split('-');
var result = [str];
for (var i = 1; i < components.length; i++) {
result.push(array1[indexOfMatching(array1, components[i], '|')]
.split('|')[1]);
}
return result.join('-');
}
// Helper to for looking up array1 and array2
function indexOfMatching(array, target, separator) {
for (var i = 0; i < array.length; i++) {
if (array[i].split(separator)[0] === target) return i;
}
return -1;
}

Categories

Resources