I have two arrays:
search array: [['#S!', 1, 1], ['#$#', 2, 5], ['#S!', 10, 12], ['#$#', 21, 5]]
and key array: ['#S!','#$#']
I want to look up into search array based on the key array element and create a resultant array which looks like this:
[[key array element,max while lookup for value at index 1 in search array, max while lookup for value at index 2 in search array], [...]]
Here is my code for the same:
let resultant = [];
keys.forEach((ele, ind) => {
resultant[ind] = [
ele,
Math.max(searchArray.filter(element => element[0] === ele)),
Math.max(searchArray.filter(element => element[0] === ele))
];
});
Now I am confused in these statements:
Math.max(newSet.filter(element => element[0] === ele)),
Math.max(newSet.filter(element => element[0] === ele))
Because filter will return the entire array but I want to find max of element with index 1 and in second statement I want to return max of element with index 2 which have the element with index 0 same as the key which I have provided.
Here is one simple test case:
search Array: [["A",1,2],["A",12,23],["A",11,23],["A",14,42],["A",71,32],["B",113,42],["B",145,62],["C",91,32],["C",14,222],["C",111,2]]
keys Array: ["A","B","C"]
Output: [["A",71,42],["B",145,62],["C",111,222]]
As you can see max corresponding to the elements are mapped to the same elements. Can someone help me with this? Is there a better or more optimized algorithm for the same than what I am using?
You could take a dynamic approach with an object for the wanted keys.
function max(search, keys) {
const temp = search.reduce((r, [key, ...data]) => {
if (!r[key]) r[key] = [key, ...data];
else data.forEach((v, i) => { if (r[key][i + 1] < v) r[key][i + 1] = v; });
return r;
}, {});
return keys.map(key => temp[key]);
}
console.log(max([['#S!', 1, 1], ['#$#', 2, 5], ['#S!', 10, 12], ['#$#', 21, 5]], ['#S!','#$#']));
console.log(max([["A", 1, 2],["A", 12, 23],["A", 11, 23], ["A", 14, 42], ["A", 71, 32], ["B", 113, 42], ["B", 145, 62], ["C", 91, 32], ["C", 14, 222], ["C", 111, 2]], ["A", "B", "C"]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try use js array flatten method to do this,
let searchArray = [["A",1,2],["A",12,23],["A",11,23],["A",14,42],["A",71,32],["B",113,42],["B",145,62],["C",91,32],["C",14,222],["C",111,2]];
let keysArray = ["A","B","C"];
console.clear();
let output = [];
keysArray.forEach(item => {
let groupSearchArray = searchArray.filter(f => f[0] === item);
let sortedArray = groupSearchArray.flat(1).filter(f => f !== item).sort().reverse();
output.push([item, sortedArray[0], sortedArray[1]]);
});
console.log(output);
Related
So, I am trying to regroup the elements... well in a way that is hard to explain. Here is a sample of input and expected output...
zip(['fred', 'barney'], [30, 40], [true, false]);
should output...
→ [['fred', 30, true], ['barney', 40, false]]
I thought reduce would be appropriate since I am supposed to take multiple arrays and convert it into a single array that contains the same amount of arrays as the input array's length...
Here is what I am working on... It isn't functioning but I believe I am close to the right idea!
function zip(array) {
return array.reduce((acc, next) => {
// get length of next array to use in for-loop...
let numOfElem = next.length
// use a for loop to access different indexed arrays...
for (let i = 0; i < numOfElem; i++) {
// this is supposed to push the appropriate element in the next array to the accumulator array's corresponding index...
acc[i].push(next[i]);
}
return acc;
}, [])
}
const result = zip(['fred', 'barney'], [30, 40], [true, false]);
console.log(result);
I believe I am attempting to push incorrectly? The idea behind acc[i].push(next[i]) is that acc[i] would create the necessary amount of arrays based off of the length of the input arrays. The code is non-functional. I am just looking for a way to get it working, even if by a different method!
Thanks for taking the time to read this and for any feedback, tips or tricks!
You could reduce the parameters and map the part result of the same index.
const
zip = (...array) =>
array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []);
console.log(zip(['fred', 'barney'], [30, 40], [true, false]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Approach for unequal lengths of arrays.
const
zip = (...array) => array.reduce((r, a, i) => {
while (r.length < a.length) r.push(Array(i).fill(undefined));
return r.map((b, j) => [...b, a[j]]);
}, []);
console.log(zip(['fred', 'barney'], [30, 40, 3, 4, 5, 6, 7], [true, false, 'don\'t know']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
function zip(...arrays) {
const flattened = arrays.flatMap(item => item)
const result = []
for (let index = 0; index <= arrays.length; index++) {
result[index] = []
for (let step = index; step < flattened.length; step = step + arrays[0].length) {
result[index][(step - index) / arrays[0].length] = flattened[step]
}
}
return result
}
const arr1 = [ 'fred', 'barney', 'alpha', 'beta' ]
const arr2 = [ 30, 40, 50, 60 ]
const arr3 = [ true, false, null, true ]
console.log(zip(arr1, arr2, arr3))
Something like this?
const zip=(arr)=>{
let res=[]
arr[0].forEach((el,k) => {
res.push(arr.reduce((acc, curr)=>{
acc.push(curr[k])
return acc
},[]))
});
return res
}
console.log(zip([['moe', 'larry', 'curly'], [30, 40, 50], [true]]))
I Have an array [[Food , quantity]] with duplicated values and i want to add the quantities of the same food in the array but i can't seem to find a way to do that
I want to do this using JavaScript and the array looks like this:
[
["Burger", 5],
["Pizza", 10],
["Coke", 13],
["Burger", 7],
["Soda", 10],
["Pizza", 4],
["Burger", 12]
]
and i want the result to be like:
[
["Burger", 24],
["Pizza", 14],
["Coke", 13],
["Soda", 10]
]
And then I want to display the result on a table
You could use reduce to group each food. Create an accumulator with each food as key and the sum of quantity as value. If the key is already added, increment it. Else, add the key with quantity as value. Then use Object.entries() to get a 2D array of food - total quantity pairs
const input=[["Burger",5],["Pizza",10],["Coke",13],["Burger",7],["Soda",10],["Pizza",4],["Burger",12]]
const counter = input.reduce((acc, [food, value]) => {
acc[food] = acc[food] + value || value;
return acc;
}, {});
const ouptut = Object.entries(counter)
console.log(JSON.stringify(ouptut))
This is what the accumulator/ counter object will look like:
{
"Burger": 24,
"Pizza": 14,
"Coke": 13,
"Soda": 10
}
You can try something like -
const arr = [["Burger" , 5], ["Pizza" , 10], ["Coke" , 13], ["Burger" , 7], ["Soda" , 10], ["Pizza" , 4], ["Burger" , 12]];
let ans = [];
arr.map((x) => {
const [name, qty] = x;
const found = ans.find((y) => y[0] === name);
if(found){
found[1] = found[1] + qty;
} else {
ans.push(x);
}
});
console.log(ans);
You may try out like,
let array = [["Burger" , 5], ["Pizza" , 10], ["Coke" , 13], ["Burger" , 7], ["Soda" , 10], ["Pizza" , 4], ["Burger" , 12]];
let itemObj = {};
for(let item of array){
if(itemObj.hasOwnProperty(item[0]))
itemObj[item[0]] += item[1];
else
itemObj[item[0]] = item[1];
}
console.log(itemObj);
let newArray = Object.keys(itemObj).map(function(key) {
return [key, itemObj[key]];
});
console.log(newArray);
I have a case that has to compare some arrays together and find the common element between all of them. The following code is working fine as long as all the array are loaded. But what if one (or even 5 of arrays) of the array is/are still empty and not loaded?
In case of having only two arrays I could do something like
if ((arr1.length > 0) && (arr2.length === 0)) {
newArr =arr1;
}
but it is going be a big conditional snippet to check all 6 arrays in this way! How can I fix this so the code runs comparison against arrays only when they are loaded and skip the array(s) when they are empty?
let newArr = [];
function common(arr1, arr2, arr3, arr4,arr5,arr6) {
newArr = arr1.filter(function(e) {
return arr2.indexOf(e) > -1 &&
arr3.indexOf(e) > -1 &&
arr4.indexOf(e) > -1 &&
arr4.indexOf(e) > -1 &&
arr5.indexOf(e) > -1 &&
arr6.indexOf(e) > -1;
});
}
common( [1, 2, 6, 5, 9,8],
[1, 2, 3, 6, 5, 9,8],
[6, 5, 4, 5,8],
[8, 2, 1, 6, 4],
[8, 2, 1, 6, 4],
//[8]
[]
);
$('div').text(newArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p> Returns Nothing Because 6th Array is Empty</p>
<div></div>
You are doing it very hard way. Below is a very simple way.You do it in following steps:
Use Rest Parameters instead of arr1,arr2,....
Remove the empty arrays using filter(). Like Array.filter(x => x.length)
Create a object which will contain keys as number. And value as their count.
Use forEach() on array of arrays.
Increment the count of the object by apply forEach() on each of array.
At last filter those keys of object which have count greater than given object.
function common(...arrays) {
let obj = {};
let temp = arrays.filter(x => x.length);
//the below line will check if all the arrays empty
if(!temp.length) console.log("All empty")
temp.forEach(arr => {
arr.forEach(x => {
obj[x] = obj[x] + 1 || 1
})
})
//console.log(temp)
//console.log(Object.keys(obj))
return Object.keys(obj).filter(a => obj[a] === temp.length).map(x => +x || x);
}
let newArr = common( [1, 2, 6, 5, 9,8],
[1, 2, 3, 6, 5, 9,8],
[6, 5, 4, 5,8,1],
[8, 2, 1, 6, 4],
[8, 2, 1, 6, 4],
//[8]
);
console.log(newArr)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div></div>
Since your code is written in ES6, there is a very ES6-way to go about your problem.
What you basically want is to check all items in the first array against n number of arrays provided in the function's parameters/arguments and only return the item that is present in all arrays.
// Use ...arrs to store all subsequent arrays in parameter
function common(arr1, ...arrs) {
return arr1.map(item => {
const nonEmptyArrs = arrs.filter(arr => arr.length);
// Go through all OTHER arrays and see if your item is present in them
const isItemFound = nonEmptyArrs.forEach(arr => {
return arr.indexOf(item) > -1;
});
// Now filter the array to remove all `false` entries
const itemFoundCount = isItemFound.filter(i => i).length;
// Only return item if it is found in ALL arrays
// i.e. itemFoundCount must match the number of arrays
if (itemFoundCount === nonEmptyArrs.length) {
return item;
}
})
.filter(item => item); // Remove undefined entries
}
See proof-of-concept below:
function common(arr1, ...arrs) {
return arr1.map(item => {
const nonEmptyArrs = arrs.filter(arr => arr.length);
const itemFoundCount = nonEmptyArrs
.map(arr => arr.includes(item))
.filter(i => i)
.length;
// Only return item if it is found in ALL arrays
if (itemFoundCount === nonEmptyArrs.length) {
return item;
}
}).filter(item => item);
}
const newArr = common([1, 2, 6, 5, 9, 8], [1, 2, 3, 6, 5, 9, 8], [6, 5, 4, 5, 8], [8, 2, 1, 6, 4], [8, 2, 1, 6, 4], []);
console.log(newArr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
My input is
let data = [
[1,2,3],
[1,3,2,4],
[3,2,1,5],
[1,2,3],
[3,2,1]
];
after this peace of code:
var dataUnique = data.reduce(function (out, item) {
return out.concat(out.filter(function (comp) {
return item.toString() == comp.toString();
}).length ? [] : [item])
}, []);
console.log(data, dataUnique);
Output give me array of 4 element
[1,2,3],
[1,3,2,4],
[3,2,1,5],
[3,2,1]
but expected output would be
[1,2,3],
[1,3,2,4],
[3,2,1,5]
Can anyone suggest any solution.
Thanks.
You can create some sort of hash — on object, Map, Set, etc and use a stringified version of your input as keys. Here's an example using a Set:
let data = [
[1,2,3],
[1,3,2,4],
[3,2,1,5],
[1,2,3],
[3,2,1]
];
let set = new Set()
let result = data.reduce((a, i) => {
let k = i.concat().sort().join('_')
if (!set.has(k)) {
set.add(k)
a.push(i)
}
return a
}, [])
console.log(result)
This could be a little simpler if you didn't mind the output having sorted versions of your input.
This is an alternative using the functions reduce, every and includes.
Basically, this approach checks if one number doesn't exist within the previously checked arrays.
let data = [ [1, 2, 3], [1, 3, 2, 4], [3, 2, 1, 5], [1, 2, 3], [3, 2, 1]],
result = data.reduce((a, c) => {
c.forEach(n => {
if (a.length == 0 || !a.every(arr => arr.includes(n))) a.push(c);
});
return a;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
What is the best way to take a multidimensional array with an unknown list of elements and group it into an object to remove repeated values in the first element of the subarray:
For example, I'd like to turn this:
const arr = [[a, 1, 4], [b, 3, 4], [c, 1, 7], [a, 2, 5], [c, 3, 5]]
Into this:
arrResult = {a:[[1, 4],[2, 5]], b:[[3, 4]], c:[[1, 7],[3, 5]]}
I thought about sorting this and then splitting it or running some kind of reduce operation but couldn't figure out exactly how to accomplish it.
You only need to use reduce (and slice), no need for sorting or splitting
var arr = [['a', 1, 4], ['b', 3, 4], ['c', 1, 7], ['a', 2, 5], ['c', 3, 5]];
var arrResult = arr.reduce((result, item) => {
var obj = result[item[0]] = result[item[0]] || [];
obj.push(item.slice(1));
return result;
}, {});
console.log(JSON.stringify(arrResult));
You can use reduce like this:
const arr = [["a", 1, 4], ["b", 3, 4], ["c", 1, 7], ["a", 2, 5], ["c", 3, 5]];
var result = arr.reduce((obj, sub) => {
var key = sub[0]; // key is the first item of the sub-array
if(obj[key]) obj[key].push(sub.slice(1)); // if the there is already an array for that key then push this sub-array (sliced from the index 1) to it
else obj[key] = [sub.slice(1)]; // otherwise create a new array that initially contain the sliced sub-array
return obj;
}, {});
console.log(result);
you could use reduce and destructuring like this:
const arr = [['a', 1, 4],['b', 3, 4],['c', 1, 7],['a', 2, 5],['c', 3, 5]]
function sub(arr) {
return arr.reduce((obj, [key, ...value]) => {
obj[key] ? obj[key].push(value) : obj[key] = [value]
return obj
}, {})
}
console.log(sub(arr));
I like this solution better because it abstracts away the collation but allows you to control how items are collated using a higher-order function.
Notice how we don't talk about the kind or structure of data at all in the collateBy function – this keeps our function generic and allows for it to work on data of any shape.
// generic collation procedure
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
// generic head/tail functions
const head = ([x,...xs]) => x
const tail = ([x,...xs]) => xs
// collate by first element in an array
const collateByFirst = collateBy (head)
// your custom function, using the collateByFirst collator
// this works much like Array.prototype.reduce
// the first argument is your accumulator, the second argument is your array value
// note the acc=[] seed value used for the initial empty collation
const foo = collateByFirst ((acc=[], xs) => [...acc, tail(xs)])
const arr = [['a', 1, 4], ['b', 3, 4], ['c', 1, 7], ['a', 2, 5], ['c', 3, 5]]
let collation = foo(arr);
console.log(collation.get('a')) // [ [1,4], [2,5] ]
console.log(collation.get('b')) // [ [3,4] ]
console.log(collation.get('c')) // [ [1,7], [3,5] ]
Of course you could write it all in one line if you didn't want to give names to the intermediate functions
let collation = collateBy (head) ((acc=[], xs) => [...acc, tail(xs)]) (arr)
console.log(collation.get('a')) // [ [1,4], [2,5] ]
Lastly, if you want the object, simply convert the Map type to an Object
let obj = Array.from(collation).reduce((acc, [k,v]) =>
Object.assign(acc, { [k]: v }), {})
console.log(obj)
// { a: [ [1,4], [2,5] ],
// b: [ [3,4] ],
// c: [ [1,7], [3,5] ] }
Higher order functions demonstrate how powerful generic procedures likes collateBy can be. Here's another example using the exact same collateBy procedure but performing a very different collation
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
const collateEvenOdd = collateBy (x => x % 2 === 0 ? 'even' : 'odd')
const sumEvenOdd = collateEvenOdd ((a=0, b) => a + b)
let data = [2,3,4,5,6,7]
let collation = sumEvenOdd (data)
let even = collation.get('even')
let odd = collation.get('odd')
console.log('even sum', even) // 2 + 4 + 6 === 12
console.log('odd sum', odd) // 3 + 5 + 7 === 15