Another javascript array challenge - javascript

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;
}

Related

checking if more elements of array have same value of key , if they have remove one?

so I've tried everything that i know. Using map and filter, prototypes. Didn't work. .
[{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}]
So what I want to achieve, is when i do ajax, with javascript, to check if two or more object have same value for type, if there is for example two or more bmw-s, remove others and and push just one object with bmw type. Hope i am clear enough.
Thanks in advance
function removeDuplicates(arr) {
var alreadyExist = {}; // hash object to keep track of elemnt that have already been encountered
var indexes = []; // array of indexes that will be removed
arr.forEach(function(o, i) { // for each object o in arr
if(alreadyExist[o.type]) // if the type of the object o at index i already exist
indexes.push(i); // mark its index i to be removed later
else // if not
alreadyExist[o.type] = true; // then mark the object as found so other ones will be removed
});
// for each index in the indexes array
for(var i = 0; i < indexes.length; i++)
arr.splice(indexes[i] - i, 1); // remove the object at that index ( - i because the array arr is continually changing. Its length decrease every time we remove an item)
}
var array = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"red","type":"bmw"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}];
removeDuplicates(array);
console.log(array);
don't remove elements, create a filtered Array:
var yourArray = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}];
var cache = {},
filteredArray = yourArray.filter(({type}) => type in cache? false: (cache[type] = true));
console.log(filteredArray);
It's non destructive, more performant and even simpler and shorter.
Edit: And even without modern features:
var filteredArray = yourArray.filter(function(item){
return item.type in this? false: (this[item.type] = true);
}, {/* the cache-object */}); //abusing `this` to pass the cache object
You can keep track of which types are already present in your array with an object, then only push into new array those that are not present:
var vehicles = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"red","type":"bmw"}, {"color":"black","type":"mercedes"}];
var uniques = [];
var types = {};
for (var i = 0; i < a.length; i++) {
if (!types[a[i].type]) { uniques.push(a[i]); }
types[a[i].type] = true;
}
//uniques = [{"color":"black","type":"bmw"},{"color":"gray","type":"golf"}, {"color":"black","type":"mercedes"}];

javascript: adding strings into an object through a class method

I'm trying to create a javascript class with a couple of methods that would let me add view and delete. I'm stuck with the add method, it should return me a unique ID of each string passed into the add method, but instead I can't figure out how to jump to a next code block when I'm done adding strings. Below is some of my current code:
var obj = {};
var arr = [];
var Words = function(){
this.add = function(newWord){
if(newWord !== false){
arr.push(newWord);
} else {
for (var i = 0; i < arr.length; i++){
obj[i] = arr[i];
return obj[i];
}
}
var words = new Words();
words.add('first');
words.add('second');
words.add('third');
I feel I should be creating a next() function of some sort that runs after I'm done pushing the last passed string, but I seem to have forgotten everything.
While I don't really know what you're trying to accomplish, there are some problems with your code...
An Object has keys and values. For every key there is a value.
Ex. mykey: "corresponding value"
for (var i = 0; i < arr.length; i++){
obj[i] = arr[i];
return obj[i];
}
You are setting obj[i] when i is an integer, so unless your keys are integers (probably not), you need to specify keys.
Otherwise, use another array for what you are doing (if possible).
If you are actually filling this object with keys that are numerals, then ignore the above.
I would also point you to using callbacks for finding out when .add() is finished (or just run your code after running .add() ??).
Why not just create an array, set it's values as you want, and retrieve them based on iteration?
var array = [];
//add
array.push("first");
//view value
console.log(array[0]); // output: "first"
//view index
array.indexOf("first"); // output 0
//delete
array.splice(0, 1);
Then wrap this as a class if you'd like:
var array = [];
var Words = function() {
this.add = function(input) {
array.push(input);
}
this.view = function(input) {
return array[input];
}
this.delete = function(input) {
array.splice(input, 1);
}
}
Hope this helps.

Removing an element from a javascript array causing issues

So I'm a little stuck as to why this isn't working. I'm trying to remove all of the empty strings in an array and it keeps giving me back an array that is just one empty string. Any ideas?
function splitNames(){
var names = document.getElementById("1").value.split("\n");
for(var i = 0; i<=names.length; i++){
if(names[i]==""){
names = names.splice(i, 1);
console.log(names);
}
}
console.log(names);
}
The string would look like this by the way.
Hi
Hello
(remove this one)
Bonjour
blah
(remove this one)
(remove this one)
blah
The array comes out to this ["Hi", "Hello","",...]
Perhaps the simplest way to do this is to use the filter function and search for truthy values. By default, empty strings are false.
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
var strings = ["zebras", "trees", "forests", "", "hi", "wizards", "", "", "lizards"];
strings = strings.filter((e) => e);
console.log(strings);
It's important to note that empty strings by default are false. However, strings that contain only whitespace characters are true. In that scenario, my example would not work and you would have to do this
strings.filter((e) => e.trim());
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
function splitNames(){
// var names = document.getElementById("1").value.split("\n");
var names = "Hi\nHello\n \nGoodBye".split("\n");
var filteredNames = names.filter(function(item){
return item.trim() !== "";
});//filter
return filteredNames;
}//splitNames()
console.log( splitNames() );
Create a new array and push what you need.
function splitNames(){
var names = document.getElementById("1").value.split("\n");
var newArr = [];
for(var i = 0; i<=names.length; i++){
if(names[i]){
newArr.push(names[i]);
}
}
return newArr.join('\n');
//console.log(names);
}
try it:
function splitNames(){
var names = document.getElementById("1").value.split("\n");
var newArr = names.filter(function(name){
return name!=="";
});
return newArr;
}

Combining elements of 2 dimentional array

I have an JavaScript array:
var arr = [["A",["05",90]],["A",["04",240]],["A",["03",235]],["B",["00",123]],["B",["01",234]]];
I want final array to look like:
var final = [["A",[["05",90],["04",240],["03",235]]],["B",[["00",123],["01",234]]]];
The final array is formed by combining all the 2nd element of 2 dimensional array when the 1st element matches.
Please advice how can this be achieved in JavaScript
Object keys are generally the easiest way to create groups like this
var tmp = {}; // temporary grouping object
// loop over data
arr.forEach(function (item) {
// check if group started
if (!tmp.hasOwnProperty(item[0])) {
tmp[item[0]] = [];
}
// push data to group
tmp[item[0]].push(item[1]);
});
// map temp object to results array
var results = Object.keys(tmp).map(function (key) {
return [key, tmp[key]];
});
DEMO
If you start with the array you gave:
var arr = [["A",["05",90]],["A",["04",240]],["A",["03",235]],["B",["00",123]],["B",["01",234]]];
then create a new array to store the values:
var final = [];
and simply combine all of the third-level elements (such as ["05",90] and ["01",234]) of each second-level ones (such as "A" and "B") by looping through the array:
for(var i = 0; i < arr.length; i++) {
var found = false;
for(var j = 0; j < final.length; j++) {
if(arr[i][0] == final[j][0]) {
final[j][1].push(arr[i][1]);
found = true;
break;
}
}
if(!found) {
final[final.length] = [arr[i][0], [[arr[i][1][0], arr[i][1][1]]]];
}
}
This is essentially a sorting method: if the "key" is equal to one in the final array, then it adds it to that one. If not, then appends it to the end of final.
Here's the working example on JSFiddle: link.
This outputs the array:
["A", [["05", 90], ["04", 240], ["03", 235]]], ["B", [["00", 123], ["01", 234]]]
as requested.
Also, as #PaulS commented, it would be recommended to use Objects instead as Strings, to make them Key-Value pairs. But in my answer I stuck with arrays.

How to convert a string to an array based on values of another array in JavaScript

I have some data that I'm trying to clean up. For the field in question, I know what the possible values are, but the value is stored in a concatenated string and I need them in an array. Here is what I would like to do:
var valid_values = ['Foo', 'Bar', 'Baz'];
var raw_data = ['BarFoo','BazBar','FooBaz'];
desired_result = [['Bar','Foo'],['Baz','Bar'],['Foo','Baz']];
I'm not sure what this is called, so I hope this isn't a duplicate.
You can iterate over each data value, searching for allowed string with indexOf or contains and returning successful matches as array.
Here's my version of code and working example at jsFiddle:
var out = raw_data.map(function (raw) {
return valid_values.filter(function (value) {
return raw.contains(value);
});
});
//out === [['Bar','Foo'],['Baz','Bar'],['Foo','Baz']];
I assumed that output match order isn't important.
This is assuming some things about your data:
you need to split strings into 2-item pairs
input & terms are case-sensitive
you won't be dealing with null/non-conforming inputs (requires more edge-cases)
In that case, you'd want to do something like this:
// for each item in the desired result, see if it's a match
// at the beginning of the string,
// then split on the string version of the valid value
function transform(input){
for(i = 0; i < len; i++) {
if (input.indexOf(valid_values[i]) === 0) {
return [ valid_values[i], input.split(valid_values[i])[1] ];
}
}
return [];
}
// to run on your input
var j = 0, raw_len = raw_data.length, desired_result = [];
for(; j < raw_len; j++) {
desired_result.push(transform(raw_data[j]));
}
This code is pretty specific to the answer you asked though; It doesn't cover many edge cases.

Categories

Resources