【问题标题】:How can I search an array of strings for a string that contains numbers from another array?如何在字符串数组中搜索包含来自另一个数组的数字的字符串?
【发布时间】:2020-02-11 20:56:01
【问题描述】:

我有两个数组:
长度为 2 的数组([0, 0] 或 [1, 2] 数组)以[x, y] 的格式表示坐标:

let coArr = [
  [[0, 0], [1, 1], [2, 2], "Mode1"],
  [[0, 0], [0, 1], [0, 2], "Mode2"]
  // and six more...
], strArr = [
  "Mode1(0, 0)",
  "Mode2(1, 2)",
  "Mode1(0, 2)" 
  // ...
  // You get the idea
  // ...
];

当strArr 包含三个字符串时,如何创建一个返回true 的函数,这三个字符串对应于coArr 中的一个数组中的数组,仅当 Mode是一样的吗? 谁能给我一些代码来解释我如何实现这一点?

这个问题很难解释,但也许举个例子会有所帮助:

如果...我想返回 true 假设:
coArr 包含 [[0, 0], [0, 1], [0, 2], "Mode2"]
并且 strArr 包含 3 个字符串:

"Mode2(0, 0)",
"Mode2(0, 1)",
"Mode2(0, 2)"

另外,有没有一种方法可以让您不需要提供坐标作为争论?

【问题讨论】:

  • 你的名字是Learn for Fun,但我没有证据表明你试图从这个问题中学习:) 你试过什么?你在找什么?一种算法或一组步骤,以可读语言说明如何解决这个问题?一个全面的工作代码解决方案?
  • @Ashley 第一)如果我现在有东西可以学习的话。 2nd)我完全不知道如何在没有Array.includes() 方法的情况下解决这个问题,这种方法对我来说永远不起作用。 3rd) 我想要一段带有解释的代码。

标签: javascript arrays string numbers


【解决方案1】:

所以,我想首先说你现在肯定有很多东西要学习。做一些尝试,找出你的方法,然后用你能提供的任何代码解释它,不管它是否有效,这总是一个好主意。你在这里的问题也不是最清楚的,所以我将提供我如何解释它的例子。我还将以某种top-down 的方式进行解释,因此需要阅读一下。

使用您提供的输入和描述,让我们处理一个函数,该函数采用模式名称,如果strArr 包含该模式在coArr 中的所有坐标,则返回true。为了澄清术语,假设如果此函数返回true,我们将说给定模式被strArr覆盖。希望到目前为止你和我在一起。

const isModeCovered = modeName => {
    let mode = coArr.find(x => x.name == modeName);
    let modeCoordinates = strArr.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

function isModeCovered(modeName) {
    let mode = coArr.find(function(x) {
        return x.name == modeName;
    });
    let modeCoordinates = strArr.filter(function(x) {
        return x.name == modeName;
    });
    return mode.isCoveredBy(modeCoordinates);
}

这里我们有一个我写的函数来做这件事。如您所见,我已经编写了两次此函数:一次使用Arrow functions,一次使用“普通”函数。我不会深入讨论 Javascript 中的箭头函数是什么,您将看到本示例的其余部分使用它们。如果您现在需要熟悉它们是一个很好的时机,但是您也可以通过比较上面的两个函数来理解它们。不管怎样,让我们​​继续看看这个函数到底在做什么。

let mode = coArr.find(x => x.name == modeName);

这里我们使用find 函数来查找coArr 中名称与给定模式名称相同的第一项。正如您在链接中看到的,find 搜索执行它的Array 的每个项目,并返回使给定回调返回 true 的第一个项目。我们给它的回调将不起作用。这是因为它假设coArr 的每个项目都是一个具有name 的对象。下一行,我们所在的 filtering strArr,也将不工作,原因类似。

我为什么要这样做?

我这样做是因为数据,以它已经给出的形式 - 直言不讳 - 令人头疼。你完全可以在不修改数据的情况下解决这个问题,但是我更喜欢的原因有很多,我稍后会解释。因此,我们将假设我们已获取给定的数据并将其解析为更可用的形式:

const modes = coArr.map(x => new Mode(x));
const allModeCoordinates = strArr.map(x => new ModeCoordinate(x));

const isModeCovered = modeName => {
    let mode = modes.find(x => x.name == modeName);
    let modeCoordinates = allModeCoordinates.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

好的,这里有一些稍微修改过的代码。我们看到它是 mapping coArr 到一个名为 Mode 的对象的新数组和 映射 strArr 到一个名为 ModeCoordinate 的对象的新数组。我们可以担心 这些对象的构造函数稍后是如何处理的,但为了清楚起见,这里简要介绍一下这些对象的外观(在伪 JS 中,这不会编译):

class Coordinate {
    int x,
    int y
}

class Mode {
    string name,
    Coordinate[] coordinates // an Array of Coordinates
}

class ModeCoordinate {
    string name,
    Coordinate coordinate
}

如果我们的数据在Modes 和ModeCoordinates 中的Arrays 中,我们可以轻松地按它们的名称搜索并找到它们的关联坐标。一旦我们可以轻松地做到这一点,大部分工作就为我们完成了,我们只需要确定函数最后一行背后的逻辑细节:

return mode.isCoveredBy(modeCoordinates);

让我们将 isCoveredBy 添加到 Mode 类中:

class Mode {
    string name,
    Coordinate[] coordinates

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }
}

这是一个简短的方法,但让我们快速完成它。我们使用for of 循环遍历Mode 的coordinates 中的每个Coordinate。然后,我们使用if 来测试我们是否可以在给定的Coordinates 的Array 中找到Coordinate(如果你还记得的话,它取自strArr,其模式名称与Mode 我们在)。如果找不到,我们会立即返回false,因为要“覆盖”模式,我们声明每个坐标 也需要存在于strArr 中。如果它在任何时候都没有返回false,我们在循环结束时返回true,因为这意味着它们都被找到了。

if 条件这一行可能会令人困惑:

!!!modeCoordinates.find(c => c.coordinate.equals(coordinate))

我们可以简单地分解一下。 modeCoordinates.find(c => c.coordinate.equals(coordinate)) 采用 modeCoordinates,如果你还记得,它是一个 ModeCoordinate Array,我们已将所有 strArr 项目映射到并通过我们所在的 Mode 的模式名称对其进行过滤,并且试图找到一个Coordinate 等于我们当前循环迭代的坐标的。如果找到,则返回该项目,如果未找到,则返回错误。前面的!!! 是常规!(或非)运算符和!! 运算符的组合,它将返回值强制为布尔值。

好的,所以现在我们知道我们必须将给定的数据解析为更好的对象类型,以使我们的查找和过滤变得轻松,我们已经创建了可以测试给定模式名称是否为 的函数覆盖。剩下的就是进行实际的解析,我们已经提到可以在我们的classes 的constructors 中完成。

class Coordinate {
    constructor(x, y) {
        this.x = parseInt(x);
        this.y = parseInt(y);
    }

    equals(coordinate) {
        return coordinate.x == this.x && coordinate.y == this.y;
    }
}

我们的Coordinate 类仅在我们的其他类内部使用,非常简单。它的构造函数接受一个 x 和一个 y 并将它们解析为整数。我添加了一个equals 函数,以便我们可以轻松比较两个Coordinates 是否相同。

class Mode {
    constructor(arr) {
        this.name = arr.pop();
        this.coordinates = arr.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
    }

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }
}

我们的Mode 类的构造函数采用Array(因为coArr 是Arrays 的Array)和pops 名称的最后一个成员。剩余的Array 是所有坐标,因此它们被映射到Coordinate 对象中。

class ModeCoordinate {
    constructor(input) {
        let inputArr = input.replace(/\s/g, "").split(/[^A-Za-z0-9]/);
        this.name = inputArr[0];
        this.coordinate = new Coordinate(inputArr[1], inputArr[2]);
    }
}

我们的ModeCoordinate 类需要一个字符串并使用regex 删除空格(使用replace)然后split 字符串并只留下字母数字字符。例如,"Mode2(1, 2)" 变为 ["Mode2", "1", "2"]。这样命名很容易将第一项存储为名称并从最后两项创建Coordinate。

现在我们可以把它们放在一起了。我添加了一些测试数据,以便您可以运行它:

class Coordinate {
    constructor(x, y) {
        this.x = parseInt(x);
        this.y = parseInt(y);
    }

    equals(coordinate) {
        return coordinate.x == this.x && coordinate.y == this.y;
    }
}

class Mode {
    constructor(arr) {
        this.name = arr.pop();
        this.coordinates = arr.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
    }

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }
}

class ModeCoordinate {
    constructor(input) {
        let inputArr = input.replace(/\s/g, "").split(/[^A-Za-z0-9]/);
        this.name = inputArr[0];
        this.coordinate = new Coordinate(inputArr[1], inputArr[2]);
    }
}

let coArr = [
    [[0, 0], [1, 1], [2, 2], "Mode1"],
    [[0, 0], [0, 1], [0, 2], "Mode2"],
    [[0, 0], [0, 1], [0, 2], "Mode3"],
    [[0, 0], [0, 1], [0, 2], "Mode4"],
    [[0, 0], [0, 1], [0, 2], "Mode5"],
    [[0, 0], [0, 1], [0, 2], "Mode6"]
  ], strArr = [
    "Mode1(0, 0)",
    "Mode1(1, 1)",
    "Mode1(2, 2)",
    "Mode2(0, 0)",
    "Mode2(1, 2)",
    "Mode2(0, 2)"
  ];

const modes = coArr.map(x => new Mode(x));
const allModeCoordinates = strArr.map(x => new ModeCoordinate(x));

const isModeCovered = modeName => {
    let mode = modes.find(x => x.name == modeName);
    let modeCoordinates = allModeCoordinates.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

console.log(isModeCovered("Mode1")); // returns true
console.log(isModeCovered("Mode2")); // returns false

如果您想查看 所有 模式是否被“覆盖”,您可以在 modes 中的所有模式上循环运行我们创建的函数。显然这里没有任何错误处理 - 我们真的希望数据采用您显示的格式。希望这能回答您的问题。

使用 cmets 中要求的替代解决方案进行编辑:

class Coordinate {
    constructor(x, y) {
        this.x = parseInt(x);
        this.y = parseInt(y);
    }

    equals(coordinate) {
        return coordinate.x == this.x && coordinate.y == this.y;
    }
}

class Mode {
    constructor(name, coordinates) {
        this.name = name;
        this.coordinates = coordinates.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
    }

    isCoveredBy(modeCoordinates) {
        for (const coordinate of this.coordinates) {
            if (!!!modeCoordinates.find(c => c.coordinate.equals(coordinate)))
                return false;   
        }
        return true;
    }

    addCoordinates(coordinates) {
        let newCoordinates = coordinates.map(coordinate =>
            new Coordinate(coordinate[0], coordinate[1])
        );
        this.coordinates.concat(newCoordinates);
    }
}

class ModeCoordinate {
    constructor(input) {
        let inputArr = input.replace(/\s/g, "").split(/[^A-Za-z0-9]/);
        this.name = inputArr[0];
        this.coordinate = new Coordinate(inputArr[1], inputArr[2]);
    }
}

let coArr = [
    [[0, 0], [1, 1], [2, 2], "Mode1"],
    [[0, 0], [2, 2], "Mode3"],
    [[0, 0], [0, 1], [0, 2], "Mode2"],
    [[1, 1], "Mode3"],
    [[0, 0], [0, 1], [0, 2], "Mode4"],
    [[0, 0], [0, 1], [0, 2], "Mode5"],
    [[0, 0], [0, 1], [0, 2], "Mode6"]
  ], strArr = [
    "Mode1(0, 0)",
    "Mode1(1, 1)",
    "Mode1(2, 2)",
    "Mode2(0, 0)",
    "Mode2(1, 2)",
    "Mode2(0, 2)",
    "Mode3(0, 0)",
    "Mode3(1, 1)",
    "Mode3(2, 2)"
  ];

const extractModes = () => {
    let modes = [];
    for (let item of coArr) {
        let name = item.pop();
        let mode = modes.find(x => x.name == name);
        if (!!mode)
            mode.addCoordinates(item);
        else 
            modes.push(new Mode(name, item));      
    }

    return modes;
};

const modes = extractModes();
const allModeCoordinates = strArr.map(x => new ModeCoordinate(x));

const isModeCovered = modeName => {
    let mode = modes.find(x => x.name == modeName);
    let modeCoordinates = allModeCoordinates.filter(x => x.name == modeName);
    return mode.isCoveredBy(modeCoordinates);
};

console.log(isModeCovered("Mode1")); // returns true
console.log(isModeCovered("Mode2")); // returns false
console.log(isModeCovered("Mode3")); // returns true

【讨论】:

  • 您在两者中都输入了"Mode1"。如果您将第二个更改为 "Mode2",它将返回 false。
  • 我真的想说这解决了我的问题but...。我尝试过使用您的解决方案,但遇到了问题。如您所见,我在coArrs 中有一个模式2 数组:[[[0, 0], [1, 0], [2, 0]], "Mode2"] 和只有一个 Mode 2 字符串strArr。你能解决这个问题吗?非常感谢。
  • 很抱歉再次打扰您,但还有一个问题。有没有办法只有两种模式?
  • 我不确定您的意思 - 这可以适应任意数量的模式,您只需将它们添加到数据中。你能解释一下吗?
  • 我的意思是,有没有一种方法可以让coArr 中有多个具有相同模式的数组并且仍然可以工作。您所做的是为每组坐标制作了许多不同的模式。有没有办法让很多组坐标具有相同的模式,但仍然可以工作?
猜你喜欢
  • 1970-01-01
  • 2012-07-20
  • 2011-08-15
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-02
相关资源
最近更新 更多