I have a function called Action which receives an array parameter like this.
[
['X','','O'],
['O','','O'],
['X','X','']
]
I want this function to return an array like this where each empty section of the previous array is filled individually with a specific value, e.g Y.
[
[
['','Y',''],
['','',''],
['','','']
],
[
['','',''],
['','Y',''],
['','','']
],
[
['','',''],
['','',''],
['','','Y']
]
]
I know I can do that with forEach but it needs nested forEach which I think isn't very optimal. Is there any better way of doing that?
This should work:
let arr = [
['X','','O'],
['O','','O'],
['X','X','']
]
let res = []
arr.map((item, index)=>{
item.map((sub, indx) =>{
if(sub === ""){
let array_=
[
['','',''],
['','',''],
['','','']
];
array_[index][indx] = "Y";
res.push(array_)
}
})
})
console.log(res)
You could avoid nesting loops with the following steps
flatten the matrix into a 1-d array
get the indices of the empty sections
create new flatten 1-d arrays with empty indices from previous result marked as 'Y'
transform flatten 1-d arrays back to 3x3 matrix
const arr = [
["X", "", "O"],
["O", "", "O"],
["X", "X", ""],
]
const res = arr
.flat() // flatten the array
.map((section, i) => [section, i])
.filter(([section, _]) => section === "")
.map(([_, i]) => i) // get the indices of empty sections
.map(sectionIdx =>
Array.from({ length: 9 }, (_, i) => (i === sectionIdx ? "Y" : ""))
) // create new flattened array with empty indices marked as 'Y'
.map(flattenedArr => [
flattenedArr.slice(0, 3),
flattenedArr.slice(3, 6),
flattenedArr.slice(6, 9),
]) // turn back flatten array into 3x3 matrix
console.log(res)
I feel compelled to give the trivial answer, which is to continue using nested .forEach. This results in very readable code, which is, in my experience, more valuable than high performance code most of the time.
I'd begin by looping over each row and column of your input. Every time you encounter an matching cell (i.e. empty string, in your case), clone the input structure (using .map to '') and replace the matching element with the desired replacement string (e.g. 'Y').
const input = [
['X','','O'],
['O','','O'],
['X','X','']
]
const result = [];
input.forEach((row, iRow) => row.forEach((col, iCol) => {
if (col == '') {
const clone = input.map(row => row.map(col => ''));
clone[iRow][iCol] = 'Y';
result.push(clone);
}
}));
console.log(result);
Related
I have a nested array like following:
[
myfield: {value1,value2},
myotherfield: {value1,value2}
]
If i have the next scenario
[
myfield: {"A","B"},
myotherfield: {"C","D"}
]
How can i iterate the arrays to get the value1 of both fields at the same time? In this case i need to get A & C on the first iteration and B & D on the second iteration.
const data = {
myfield: ["A","B"],
myotherfield: ["C","D"]
};
data.myfield.forEach((fieldA, index) => {
const fieldB = data.myotherfield[index];
console.log(fieldA, fieldB);
})
Note1: Use this if both entries have the same length.
Note2: If both entries can have a different length then you need to check what of the two has the highest length while making the other entries return null.
const data = A.map((data_A, index) => {
const data_B = B[index]
console.log("data", data_A, data_B )
})
I have a Array of String array i want to fetch the unique elements from that array ignoring the case. how can i fetch
Example:
List of array:
[
["java,C,Node,ReactJs"]
["c","Spring"]
["undefined"]
["asdja"]
["Java","Spring"]
["adjasjh"]
["adjasjh"]
["java"]
]
ExpectedList:
[java,C,node,reactjs,spring,..]
Join the array, change to lower case, split by comma, convert into a Set to get the unique words, and then spread back to an array:
const arr = [["java,NO,C,Node,ReactJs"],["c","Spring"],["undefined"],["asdja"],["Java","Spring"],["adjasjh", "Spring-roll"],["adjasjh"],["java", "javascript", "java-script"]];
const result = [...new Set(arr.join().toLowerCase().split(','))];
console.log(result);
Older answer - won't work in all cases:
You can join the array of arrays, and use String.match() with negative lookahead RegExp to get unique strings:
const arr = [["java,NO,C,Node,ReactJs"],["c","Spring"],["undefined"],["asdja"],["Java","Spring"],["adjasjh", "Spring-roll"],["adjasjh"],["java", "javascript", "java-script"]];
const result = arr.join()
.match(/([^,]+\b)(?!.+\1[,|$])/ig);
console.log(result);
You can use reduce and map methods to iterate array and words and also Set to return array of unique elements in the end.
const array = [["java,C,Node,ReactJs"], ["c","Spring"], ["undefined"], ["asdja"], ["Java","Spring"], ["adjasjh"], ["adjasjh"], ["java"] ]
const result = [...new Set(array.reduce((r, e) => {
return r.concat(...e.map(s => s.split(',').map(w => w.toLowerCase())))
}, []))]
console.log(result)
You can flatten your array, convert every value to lowercase, use Set to remove duplicates, and Array.from to convert it back to an array.
If your ["java,C,Node,ReactJs"] is actually: ["java","C","Node","ReactJs"] you can do it this way:
const array = [
["java","C","Node","ReactJs"],
["c","Spring"],
["undefined"],
["asdja"],
["Java","Spring"],
["adjasjh"],
["adjasjh"],
["java"]
];
const unique = Array.from(
new Set([].concat.apply([], array).map(item => item.toLowerCase()))
// ^^^ flatten array
);
console.log(unique);
You could flat the data and use a Map for keeping the first unique value without mutating it.
const flat = (r, a) => Array.isArray(a) ? a.reduce(flat, r) : r.concat(a.split(','));
var data = [["java,C,Node,ReactJs"], ["c", "Spring"], ["undefined"], ["asdja"], ["Java", "Spring"], ["adjasjh"], ["adjasjh"], ["java"]],
unique = Array.from(data
.reduce(flat, [])
.reduce((m, v) => ((l => m.has(l) || m.set(l, v))(v.toLowerCase()), m), new Map)
.values()
);
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }
let's say i have this code :
let variants = []
let variant = {
variantName: 'Size',
variantItems: ['XL','MD','SM']
}
variants.push(variant)
variant = {
variantName: 'Color',
variantItems: ['Red','Blue']
}
variants.push(variant)
okay, now how do i output it to something like this :
and the variants array can contain many variant object
and if there is one more object inside variants array :
variant = {
variantName: 'Material',
variantItems: ['Plastic','Wood', 'Ceramic']
}
variants.push(variant)
and it will be outputted like this :
Please help, i very appreciate your help...
You can use a recursive function with Array#map, and Array#concat to flatten the results:
const variants = [{"variantName":"Size","variantItems":["XL","MD","SM"]},{"variantName":"Color","variantItems":["Red","Blue"]},{"variantName":"Material","variantItems":["Plastic","Wood","Ceramic"]}];
const addVariants = (variants) => {
// destructure the 1st item array, and the rest of the variants
const add = ([{ variantName, variantItems }, ...variants], row = []) =>
// iterate the variants and flatten
[].concat(...variantItems.map((variantItem) => {
// create a new array for the current row, and the variant string
const currRow = [...row, `${variantName}: ${variantItem}`];
// if there are more variants, invoke add with the remaining variants and the current row, or join the array to a string (the end result)
return variants.length ? add(variants, currRow) : currRow.join(', '); // instead of currRow.join(', ') replace with [currRow] if you want an array
}));
return add(variants);
}
const result = addVariants(variants);
console.log(result);
This is a functional ES6 approach.
let variants = [{
variantName: "Size",
variantItems: [
"XL",
"MD",
"SM"
]
},
{
variantName: "Color",
variantItems: [
"Red",
"Blue"
]
}];
let crossJoined = new Array(variants.reduce((product, variant) => (product * variant.variantItems.length), 1))
.fill(0)
.reduce(crossJoin => {
crossJoin.data.push(crossJoin.currentIndexes.map((itemIndex, variantIndex) => `${variants[variantIndex].variantName}: ${variants[variantIndex].variantItems[itemIndex]}`).join(", "));
let incrementableIndex = variants.length - crossJoin.currentIndexes
.slice()
.reverse()
.findIndex((itemIndex, variantIndex) => variants[variants.length - variantIndex - 1].variantItems.length > itemIndex + 1) - 1;
crossJoin.currentIndexes[incrementableIndex]++;
crossJoin.currentIndexes = crossJoin.currentIndexes.map((value, index) => (index > incrementableIndex
? 0
: value));
return (crossJoin.currentIndexes.length == variants.length
? crossJoin
: crossJoin.data);
}, {
data: [],
currentIndexes: new Array(variants.length).fill(0)
}).join("\n");
console.log(crossJoined);
First, an array is created with a length of all variantItems array lengths multiplied, then it is zero-filled. Next, we reduce it to another array.
crossJoin is the aggregator that holds an object of this structure most of the time:
{
data: [ … ],
currentIndexes: [ 1, 3 ] // Corresponds to variants[0].variantItems[1]
// and variants[1].variantItems[3]
}
That is “most of the time”, until the end, where currentIndexes won’t be used anymore and only its data property is returned.
So in each reduce iteration, we first push a new entry to crossJoin.data, using crossJoin.currentIndexes and the indexes of that array. Removing .join(", ") will result in an entry of the structure
[ "Size", "XL" ]
Next, we need to increment the numbers in crossJoin.currentIndexes:
[ 0, 0 ] should be incremented to [ 0, 1 ], because there is a second color at the index 1. [ 0, 1 ] should be incremented to [ 1, 0 ], because there is no third color, but a second size, but then we need the first color again, and so on. The last valid index array is [ 2, 1 ] which corresponds to the third size and the second color, which is the last combination.
incrementableIndex is the last possible index that can still be incremented. Once incremented, all subsequent indexes have to be 0.
You see those variants.length - something - 1 twice, because you need to find the first index from the end, so you have to reverse (a copy of — hence the slice) the array, then re-interpret the found index as an index from the start again.
The return returns this crossJoin object for the next iteration. The condition crossJoin.currentIndexes.length == variants.length applies to the very end, where no index can be incremented anymore. It adds a NaN to the currentIndexes array, so the lengths don’t match, and instead of filtering out or preventing the NaN, I just ignored and discarded it altogether.
The output will be an array of all combinations. You can .join("\n") it to make a string where each combination is separated by a line-break, or you can use .forEach to append each combination to a list, for example.
Use .each loop on variants array. Take another array and add object using size, color and material. So it will become combined array of your requirement. Then print new array data as per requirement using .each loop .
I need to get elements from an array of objects where one of that object's properties (name in this case) is duplicated--in other words, appears in some other object in the array.
data
var data = [
{id:1, name:"sam", userid:"ACD"},
{id:1, name:"ram", userid:"SDC"},
{id:1, name:"sam", userid:"CSTR"}
];
i need to check all row and get all the array value where name property is duplicating.
the expected output:
[
{id:1, name:"sam", userid:"ACD"},
{id:1, name:"sam", userid:"CSTR"}
]
my code
Array.from(data).map(x => x.name)
but it is returning all the values.
The code should not create any performance issue because array will contain more than 500 rows.
Angular is a framework, not a language. There is no Angular in your problem.
Let me understand if I understood well. You have an array of objects and you want to keep all the elements that are duplicate and get rid of others, all right? You can try:
data.reduce((acc, value, i, arr) => {
if(acc.some(v => v.name === value.name)) return acc;
let filtered = arr.filter(v => v.name === value.name);
return filtered.length > 1 ? acc.concat(filtered) : acc;
}, []);
Or you can sort your array in first instance, in order to improve performance:
const sort = (a, b) => a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1;
let duplicates = [];
let sortedArray = data.sort(sort);
for(let i=0; i<sortedArray.length - 1; i++) {
if(sortedArray[i].name === sortedArray[i+1].name) {
duplicates.push(sortedArray[i], sortedArray[i+1]);
i++;
}
}
The brute force approach would be to filter the array to keep only those elements with duplicated names, as expressed by the filter function duplicateName.
// Is there more than one element in an array satisfying some predicate?
const hasMultiple = (arr, pred) => arr.filter(pred).length > 1;
// Is this element a duplicate in the context of the array?
const duplicateName = (elt, idx, arr) => hasMultiple(arr, e => e.name === elt.name);
// Test data.
var data = [
{id:1,name:"sam", userid:"ACD"},
{id:1,name:"ram", userid:"SDC"},
{id:1,name:"sam", userid:"CSTR"}
];
console.log(data.filter(duplicateName));
However, this is going to have poor performance (O(n^2)) in the case of many elements. To solve that problem, you're going to need to preprocess the array. We'll create an object with a property for each name, whose value is an array of all the elements in which that name occurs. This operation is usually called groupBy. Several popular libraries such as underscore will provide this for you. We'll write our own. After grouping, we will filter the object of groups to remove those with only one member.
// Group an array by some predicate.
const groupBy = (arr, pred) => arr.reduce((ret, elt) => {
const val = pred(elt);
(ret[val] = ret[val] || []).push(elt);
return ret;
}, {});
// Filter an object, based on a boolean callback.
const filter = (obj, callback) => Object.keys(obj).reduce((res, key) => {
if (callback(obj[key], key, obj)) res[key] = obj[key];
return res;
}, {});
// Remove groups with only one element.
const removeNonDups = groups => filter(groups, group => group.length > 1);
// Test data.
var data = [
{id:1,name:"sam", userid:"ACD"},
{id:1,name:"ram", userid:"SDC"},
{id:1,name:"sam", userid:"CSTR"}
];
console.log(removeNonDups(groupBy(data, elt => elt.name)));
[["Django UnChainers","AA-24010"],["General","AA-26191"]]
I have one array in above form. I want to retrieve all the value with prefix AA (which are in the second position). Is there any way where I can fetch the value by passing prefix?.
I know the way where I can get the value by passing index but may be tomorrow index can get change so is it possible to fetch the value by passing prefix?
In case OP wants a function to do this.
function(arr, pattern){
return arr.map(function(x){
return x.filter( word => ~ word.indexOf(pattern))
});
}
var arr =
[ [ "Django UnChainers", "AA-24010" ], [ "General", "AA-26191" ]];
var list = arr.map(function(x){
if(~(x[1].indexOf('AA'))){
return x[1];
}
});
console.log(list);
In case the index changes in future, iterate through each string and check for the "AA" string. Check the below code.
var arr =
[ [ "Django UnChainers", "AA-24010" ], [ "General", "AA-26191" ]];
var list = arr.map(function(x){
return x.filter( word => ~ word.indexOf('AA'))
});
console.log(list);
this is shorter
var = [nested array]
a.filter(x => x[1].startsWith('AA'))
//in case you are not sure about the index
a.filter(x => x.filter(y => y.startsWith('AA').length > 0))